security-oauth2(一)

前提:在security基础上,不会的可以参考我的另一篇文章

搭建授权服务器

OAuth2 协议一共支持 4 种不同的授权模式:

授权码模式:常见的第三方平台登录功能基本都是使用这种模式。

简化模式:简化模式是不需要客户端服务器参与,直接在浏览器中向授权服务器申请令牌(token),一般如果网站是纯静态页面则可以采用这种方式。

密码模式:密码模式是用户把用户名密码直接告诉客户端,客户端使用说这些信息向授权服务器申请令牌(token)。这需要用户对客户端高度信任,例如客户端应用和服务提供商就是同一家公司,我们自己做前后端分离登录就可以采用这种模式。

客户端模式:客户端模式是指客户端使用自己的名义而不是用户的名义向服务提供者申请授权,严格来说,客户端模式并不能算作 OAuth 协议要解决的问题的一种解决方案,但是,对于开发者而言,在一些前后端分离应用或者为移动端提供的认证授权服务器上使用这种模式还是非常方便的。

     +--------+                               +---------------+
     |        |--(A)- 授权请求               ->|     资源      |
     |        |                               |     Owner     |
     |        |<-(B)-- Authorization Grant ---|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(C)-- Authorization Grant -->| Authorization |
     | Client |                               |     Server    |
     |        |<-(D)----- Access Token -------|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(E)----- Access Token ------>|    Resource   |
     |        |                               |     Server    |
     |        |<-(F)--- Protected Resource ---|               |
     +--------+                               +---------------+
(A) 客户端向资源所有者请求授权。
        授权请求可以直接发送给资源所有者(
        如图所示),或者最好通过授权
        服务器作为中介间接发送。

(B) 客户端接收到授权授权,它是
        代表资源所有者授权的凭证,
        使用本规范中定义的四种授权类型之一
        或使用扩展授权类型表示。
        授权类型取决于
        客户端请求授权的方式和授权服务器支持的类型
        。

(C) 客户端通过与授权服务器进行身份验证并提供授权许可
   来请求访问令牌。

(D) 授权服务器对客户端进行身份验证并验证
        授权授予,如果有效,则颁发访问令牌。
(E) 客户端从资源
        服务器请求受保护的资源,并通过提供访问令牌进行身份验证。

(F) 资源服务器验证访问令牌,如果有效,则为
        请求提供服务。

配置

  • 可以用 @EnableAuthorizationServer 注解并继承 AuthorizationServerConfifigurerAdapter 来配置 OAuth2.0 授权
    服务器。

/**
 * oauth2
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    略。。。
}
AuthorizationServerConfifigurerAdapter 要求配置以下几个类,这几个类是由 Spring 创建的独立的配置对象,它们
会被 Spring 传入 AuthorizationServerConfifigurer 中进行配置
    public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
        public AuthorizationServerConfigurerAdapter() {}
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {}
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {}
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {}
    }
ClientDetailsServiceConfifigurer :用来配置客户端详情服务( ClientDetailsService ),客户端详情信息在
这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
AuthorizationServerEndpointsConfifigurer :用来配置令牌(
token )的访问端点和令牌服务 (token
services)
AuthorizationServerSecurityConfifigurer :用来配置令牌端点的安全约束
  • 配置客户端详细信息

ClientDetailsServiceConfifigurer 能够使用内存或者 JDBC 来实现客户端详情服务( ClientDetailsService ),
ClientDetailsService 负责查找 ClientDetails ,而 ClientDetails 有几个重要的属性如下列表:
  • clientId:(必须的)用来标识客户的Id
  • secret:(需要值得信任的客户端)客户端安全码,如果有的话
  • scope:用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围。
  • authorizedGrantTypes:此客户端可以使用的授权类型,默认为空。
  • authorities:此客户端可以使用的权限(基于Spring Security authorities)。
客户端详情( Client Details )能够在应用程序运行的时候进行更新,可以通过访问底层的存储服务(例如将客户端详情存储在一个关系数据库的表中,就可以使用 JdbcClientDetailsService )或者通过自己实现
ClientRegistrationService 接口(同时你也可以实现 ClientDetailsService 接口)来进行管理。
我们暂时使用内存方式存储客户端详情信息,配置如下:
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    /**
     * 配置客户端认证(谁来申请令牌)使用内存
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        //暂时使用内存
        clients.inMemory() //使用内存
                .withClient("c1") //client_id
                .secret(passwordEncoder.encode("secret")) //密钥
                .resourceIds("res1","res2") //可使用的资源列表
                // 该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials
                .authorizedGrantTypes("authorization_code", "password","client_credentials","implicit","refresh_token")
                .scopes("all")// 允许的授权范围,一个字符串
                .autoApprove(false)//false跳转到授权页面
                //加上验证回调地址
                .redirectUris("http://www.baidu.com");

    }

管理令牌

        AuthorizationServerTokenServices 接口定义了一些操作使得你可以对令牌进行一些必要的管理,令牌可以被用来加载身份信息,里面包含了这个令牌的相关权限。
        自己可以创建 AuthorizationServerTokenServices 这个接口的实现,则需要继承 DefaultTokenServices 这个类,里面包含了一些有用实现,你可以使用它来修改令牌的格式和令牌的存储。默认的,当它尝试创建一个令牌的时候,是使用随机值来进行填充的,除了持久化令牌是委托一个 TokenStore 接口来实现以外,这个类几乎帮你做了所有的事情。并且 TokenStore 这个接口有一个默认的实现,它就是 InMemoryTokenStore ,如其命名,所有的令牌是被保存在了内存中。除了使用这个类以外,你还可以使用一些其他的预定义实现,下面有几个版本,它们都实现了TokenStore 接口:
  • InMemoryTokenStore:这个版本的实现是被默认采用的,它可以完美的工作在单服务器上(即访问并发量 压力不大的情况下,并且它在失败的时候不会进行备份),大多数的项目都可以使用这个版本的实现来进行 尝试,你可以在开发的时候使用它来进行管理,因为不会被保存到磁盘中,所以更易于调试。
  • JdbcTokenStore:这是一个基于JDBC的实现版本,令牌会被保存进关系型数据库。使用这个版本的实现时, 你可以在不同的服务器之间共享令牌信息,使用这个版本的时候请注意把"spring-jdbc"这个依赖加入到你的 classpath当中。
  • JwtTokenStore:这个版本的全称是 JSON Web Token(JWT),它可以把令牌相关的数据进行编码(因此对 于后端服务来说,它不需要进行存储,这将是一个重大优势),但是它有一个缺点,那就是撤销一个已经授权令牌将会非常困难,所以它通常用来处理一个生命周期较短的令牌以及撤销刷新令牌(refresh_token)。 另外一个缺点就是这个令牌占用的空间会比较大,如果你加入了比较多用户凭证信息。JwtTokenStore 不会保存任何数据,但是它在转换令牌值以及授权信息方面与 DefaultTokenServices 所扮演的角色是一样的。

1、定义TokenConfifig

confifig 包下定义 TokenConfifig ,我们暂时先使用 InMemoryTokenStore ,生成一个普通的令牌
package com.security.oauth2.oauth2service.config.oauth2;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

@Configuration
public class TokenConfig {

    /**
     * 令牌策略-内存方式
     * @return
     */
    @Bean
    public TokenStore tokenStore(){
        //内存方式
        return new InMemoryTokenStore();
    }
}

2、定义AuthorizationServerTokenServices

AuthorizationServer 中定义 AuthorizationServerTokenServices:

    //注入令牌策略-内存方式
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private ClientDetailsService clientDetailsService;
    
    /**
     * 配置令牌服务
     */
    @Bean
    public AuthorizationServerTokenServices tokenService(){
        DefaultTokenServices services = new DefaultTokenServices();
        //使用客户端配置
        services.setClientDetailsService(clientDetailsService);
        //是否使用刷新令牌
        services.setSupportRefreshToken(true);
        //令牌的存储策略
        services.setTokenStore(tokenStore);
        //令牌默认有效期2小时
        services.setAccessTokenValiditySeconds(7200);
        //刷新令牌默认有效期3天
        services.setRefreshTokenValiditySeconds(259200);
        return services;
    }

令牌访问端点配置

        AuthorizationServerEndpointsConfifigurer 这个对象的实例可以完成令牌服务以及令牌 endpoint 配置。
配置授权类型( Grant Types
        AuthorizationServerEndpointsConfifigurer 通过设定以下属性决定支持的 授权类型( GrantTypes :
  • authenticationManager:认证管理器,当你选择了资源所有者密码(password)授权类型的时候,请设置这个属性注入一个 AuthenticationManager 对象。
  • userDetailsService:如果你设置了这个属性的话,那说明你有一个自己的 UserDetailsService 接口的实现,或者你可以把这个东西设置到全局域上面去(例如 GlobalAuthenticationManagerConfifigurer 这个配置对象),当你设置了这个之后,那么 "refresh_token" 即刷新令牌授权类型模式的流程中就会包含一个检查,用来确保这个账号是否仍然有效,假如说你禁用了这个账户的话。
  • authorizationCodeServices:这个属性是用来设置授权码服务的(即 AuthorizationCodeServices 的实例对象),主要用于 "authorization_code" 授权码类型模式。
  • implicitGrantService:这个属性用于设置隐式授权模式,用来管理隐式授权模式的状态。
  • tokenGranter:当你设置了这个东西(即 TokenGranter 接口实现),那么授权将会交由你来完全掌控,并且会忽略掉上面的这几个属性,这个属性一般是用作拓展用途的,即标准的四种授权模式已经满足不了你的需求的时候,才会考虑使用这个。
配置授权端点的 URL Endpoint URLs ):
        AuthorizationServerEndpointsConfifigurer 这个配置对象有一个叫做 pathMapping() 的方法用来配置端点 URL 链接,它有两个参数:
  • 第一个参数:String 类型的,这个端点URL的默认链接。
  • 第二个参数:String 类型的,你要进行替代的URL链接。

以上的参数都将以 "/" 字符为开始的字符串,框架的默认URL链接如下列表,可以作为这个 pathMapping() 方法的第一个参数:

  • /oauth/authorize:授权端点。
  • /oauth/token:令牌端点。
  • /oauth/confifirm_access:用户确认授权提交端点。
  • /oauth/error:授权服务错误信息端点。
  • /oauth/check_token:用于资源服务访问的令牌解析端点。
  • /oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话。

需要注意的是授权端点这个 URL 应该被 Spring Security 保护起来只供授权用户访问 .
AuthorizationServer 配置令牌访问端点
    //授权码服务 使用内存
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(){
        //使用内存
        return new InMemoryAuthorizationCodeServices();
    }

    //在security配置文件WebSecurityConfig 中加入bean容器
    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 配置令牌的访问端点(申请令牌的地址)和令牌服务(令牌怎么发放)
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager)//认证管理器,密码模式需要
                .authorizationCodeServices(authorizationCodeServices())//授权码服务
                .tokenServices(tokenService())//令牌管理服务
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

令牌端点的安全约束

AuthorizationServerSecurityConfifigurer :用来配置令牌端点 (Token Endpoint) 的安全约束,在
AuthorizationServer 中配置如下 .
    /**
     * 令牌的安全约束(验证有没有资格申请令牌)
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()")                    //oauth/token_key是公开
                .checkTokenAccess("permitAll()")                  //oauth/check_token公开
                .allowFormAuthenticationForClients();			//表单认证(申请令牌)
    }

在security配置文件WebSecurityConfig 中加入认证管理器bean容器

    //认证管理器,oauth2需要,放入容器
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
完整配置:
package com.security.oauth2.oauth2service.config.oauth2;


import com.security.oauth2.oauth2service.config.security.MD5PasswordEncoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
 * oauth2
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 配置客户端认证(谁来申请令牌)使用内存
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        //暂时使用内存
        clients.inMemory() //使用内存
                .withClient("c1") //client_id
                .secret(passwordEncoder.encode("secret")) //密钥
                .resourceIds("res1","res2") //可使用的资源列表
                // 该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials
                .authorizedGrantTypes("authorization_code", "password","client_credentials","implicit","refresh_token")
                .scopes("all")// 允许的授权范围,一个字符串
                .autoApprove(false)//false跳转到授权页面
                //加上验证回调地址
                .redirectUris("http://www.baidu.com");

    }



    //注入令牌策略-内存方式
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private ClientDetailsService clientDetailsService;

    /**
     * 配置令牌服务
     */
    @Bean
    public AuthorizationServerTokenServices tokenService(){
        DefaultTokenServices services = new DefaultTokenServices();
        //使用客户端配置
        services.setClientDetailsService(clientDetailsService);
        //是否使用刷新令牌
        services.setSupportRefreshToken(true);
        //令牌的存储策略
        services.setTokenStore(tokenStore);
        //令牌默认有效期2小时
        services.setAccessTokenValiditySeconds(7200);
        //刷新令牌默认有效期3天
        services.setRefreshTokenValiditySeconds(259200);
        return services;
    }

    //授权码服务 使用内存
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(){
        //使用内存
        return new InMemoryAuthorizationCodeServices();
    }

    //在security配置文件WebSecurityConfig 中加入bean容器
    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 配置令牌的访问端点(申请令牌的地址)和令牌服务(令牌怎么发放)
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager)//认证管理器,密码模式需要
                .authorizationCodeServices(authorizationCodeServices())//授权码服务
                .tokenServices(tokenService())//令牌管理服务
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

    /**
     * 令牌的安全约束(验证有没有资格申请令牌)
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()")                    //oauth/token_key是公开
                .checkTokenAccess("permitAll()")                  //oauth/check_token公开
                .allowFormAuthenticationForClients();			//表单认证(申请令牌)
    }

}

授权服务配置总结 :授权服务配置分成三大块,可以关联记忆。
既然要完成认证,它首先得知道客户端信息从哪儿读取,因此要进行客户端详情配置。
既然要颁发 token ,那必须得定义 token 的相关 endpoint ,以及 token 如何存取,以及客户端支持哪些类型的
token
既然暴露除了一些 endpoint ,那对这些 endpoint 可以定义一些安全上的约束等

启动访问

授权码方式:

 

1 )资源拥有者打开客户端,客户端要求资源拥有者给予授权,它将浏览器被重定向到授权服务器,重定向时会
附加客户端的身份信息。如:
/uaa/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com
参数列表如下:
  • client_id:客户端准入标识。
  • response_type:授权码模式固定为code
  • scope:客户端权限。
  • redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)

2 )浏览器出现向授权服务器授权页面,之后将用户同意授权。
3 )授权服务器将授权码( AuthorizationCode )转经浏览器发送给 client( 通过 redirect_uri)
4 )客户端拿着授权码向授权服务器索要访问 access_token ,请求如下:
/uaa/oauth/token?
client_id=c1&client_secret=secret&grant_type=authorization_code&code=5PgfcD&redirect_uri=http://w
ww.baidu.com
参数列表如下
  • client_id:客户端准入标识。
  • client_secret:客户端秘钥。
  • grant_type:授权类型,填写authorization_code,表示授权码模式
  • code:授权码,就是刚刚获取的授权码,注意:授权码只使用一次就无效了,需要重新申请。
  • redirect_uri:申请授权码时的跳转url,一定和申请授权码时用的redirect_uri一致。

5 )授权服务器返回令牌 (access_token)
        这种模式是四种模式中最安全的一种模式。一般用于client Web 服务器端应用或第三方的原生 App 调用资源服务的时候。因为在这种模式中access_token 不会经过浏览器或移动端的 App ,而是直接从服务端去交换,这样就最大限度的减小了令牌泄漏的风险。

测试

浏览器访问认证页面:


输入zhangsan  123(注意数据库user表,账号密码对应,密码为md5加密串)

 

点击授权

确认授权后,会自动跳转到百度页面,路径上带着code,这就是申请令牌的code了

 

 然后postman 用post方法访问:

http://localhost:8081/demo2/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=KMi0xa&redirect_uri=http://www.baidu.comhttp://localhost:8081/demo2/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=KMi0xa&redirect_uri=http://www.baidu.com

 

 简化模式:

 

1 )资源拥有者打开客户端,客户端要求资源拥有者给予授权,它将浏览器被重定向到授权服务器,重定向时会 附加客户端的身份信息。如:
/uaa/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com
参数描述同 授权码模式 ,注意 response_type=token ,说明是简化模式。
2 )浏览器出现向授权服务器授权页面,之后将用户同意授权。
3 )授权服务器将授权码将令牌( access_token )以 Hash 的形式存放在重定向 uri fargment 中发送给浏览 器。
注:
fragment 主要是用来标识 URI 所标识资源里的某个资源,在 URI 的末尾通过 ( # )作为 fragment 的开头,其中 # 不属于 fragment 的值。如 https://domain/index#L18 这个 URI L18 就是 fragment 的值。大家只需要知道js 通过响应浏览器地址栏变化的方式能获取到 fragment 就行了。
一般来说,简化模式用于没有服务器端的第三方单页面应用,因为没有服务器端就无法接收授权码。

测试:

浏览器访问认证页面:

 

输入zhangsan 123

 

确认授权后,浏览器会重定向到指定路径( oauth_client_details 表中的 web_server_redirect_uri )并以 Hash 的形式存放在重定向uri fargment , 如:

 密码模式

1 )资源拥有者将用户名、密码发送给客户端
2 )客户端拿着资源拥有者的用户名、密码向授权服务器请求令牌( access_token, 请求如下:
/uaa/oauth/token?
client_id=c1&client_secret=secret&grant_type=password&username=shangsan&password=123
参数列表如下:
  • client_id:客户端准入标识。
  • client_secret:客户端秘钥。
  • grant_type:授权类型,填写password表示密码模式
  • username:资源拥有者用户名。
  • password:资源拥有者密码。
3 )授权服务器将令牌( access_token )发送给 client
这种模式十分简单,但是却意味着直接将用户敏感信息泄漏给了 client ,因此这就说明这种模式只能用于 client 是我们自己开发的情况下。因此密码模式一般用于我们自己开发的,第一方原生App 或第一方单页面应用。

测试:

 

客户端模式

 

1 )客户端向授权服务器发送自己的身份信息,并请求令牌( access_token
2 )确认客户端身份无误后,将令牌( access_token )发送给 client ,请求如下:

/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials

参数列表如下:
  • client_id:客户端准入标识。
  • client_secret:客户端秘钥。
  • grant_type:授权类型,填写client_credentials表示客户端模式
这种模式是最方便但最不安全的模式。因此这就要求我们对 client 完全的信任,而 client 本身也是安全的。因此这种模式一般用来提供给我们完全信任的服务器端服务。比如,合作方系统对接,拉取一组用户信息。

测试:

http://localhost:8081/demo2/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials

 

整合数据库:

添加表:

DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (
  `client_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '客户端标 识',
  `resource_ids` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '接入资源列表',
  `client_secret` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '客户端秘钥',
  `scope` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `authorized_grant_types` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `web_server_redirect_uri` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `authorities` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `access_token_validity` int(11) DEFAULT NULL,
  `refresh_token_validity` int(11) DEFAULT NULL,
  `additional_information` longtext CHARACTER SET utf8 COLLATE utf8_general_ci,
  `create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
  `archived` tinyint(4) DEFAULT NULL,
  `trusted` tinyint(4) DEFAULT NULL,
  `autoapprove` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '接入客户端信息' ROW_FORMAT = Dynamic;

INSERT INTO `oauth_client_details` VALUES ('c1', 'res1', '5EBE2294ECD0E0F08EAB7690D2A6EE69', 'ROLE_ADMIN,ROLE_USER,ROLE_API', 'client_credentials,password,authorization_code,implicit,refresh_token', 'http://www.baidu.com', NULL, 7200, 259200, NULL, '2022-04-25 11:18:26', 0, 0, 'false');
INSERT INTO `oauth_client_details` VALUES ('c2', 'res2', '5EBE2294ECD0E0F08EAB7690D2A6EE69', 'ROLE_API', 'client_credentials,password,authorization_code,implicit,refresh_token', 'http://www.baidu.com', NULL, 31536000, 2592000, NULL, '2022-04-25 11:18:28', 0, 0, 'false');
 

 DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code`  (
  `create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `authentication` blob,
  INDEX `code_index`(`code`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'Spring Security OAuth2使用,用来存储授权码' ROW_FORMAT = Compact;

修改AuthorizationServer中的客户认证配置:

 

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private DataSource dataSource;

    @Bean
    public ClientDetailsService clientDetails() {
        //使用数据库
        ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        ((JdbcClientDetailsService) clientDetailsService)
                //配置密匙加密为md5
                .setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    /**
     * 配置客户端认证(谁来申请令牌)使用数据库
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());
    }

修改授权码配置跟令牌配置:

    //注入令牌策略-内存方式
    @Autowired
    private TokenStore tokenStore;

    /**
     * 配置令牌服务
     */
    @Bean
    public AuthorizationServerTokenServices tokenService(){
        DefaultTokenServices services = new DefaultTokenServices();
        //使用客户端配置
        services.setClientDetailsService(clientDetails());
        //是否使用刷新令牌
        services.setSupportRefreshToken(true);
        //令牌的存储策略
        services.setTokenStore(tokenStore);
        //令牌默认有效期2小时
        services.setAccessTokenValiditySeconds(7200);
        //刷新令牌默认有效期3天
        services.setRefreshTokenValiditySeconds(259200);
        return services;
    }

    //授权码服务 使用数据库
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(){
        //使用数据库
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    //在security配置文件WebSecurityConfig 中加入bean容器
    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 配置令牌的访问端点(申请令牌的地址)和令牌服务(令牌怎么发放)
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager)//认证管理器,密码模式需要
                .authorizationCodeServices(authorizationCodeServices())//授权码服务
                .tokenServices(tokenService())//令牌管理服务
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

用密码模式验证:

http://localhost:8081/demo2/oauth/token?client_id=c1&client_secret=secret&username=zhangsan&password=123&grant_type=password

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值