29.SpringSecurity-令牌配置

前言

image.png

  1. 到目前为止,咱们已经把自定义的3种方式嫁接到认证服务器上,经过这种方式咱们能够了发送OAuth令牌。目前token的生成和存储是按照Spring默认的一套机制运做。可是这种默认的机制并不符合咱们的需求。
  2. 从这一节开始咱们看一下对Token处理的相关知识点。怎样定制这个token,怎样生成和存储这个token。
  3. 咱们主要讲解3个基本点:
    a.基本的Token参数配置
    b.使用JWT替换默认的Token
    c.扩展和解析JWT的信息

内容

1.基本的Token参数配置

咱们以前的token生成处理都是在Authentication Server里面作的处理。 首先咱们须要修改咱们以前定义的Authentication Server:MyAuthorizationServerConfig以前是使用注解@EnableAuthorizationServer开启默认的Spring Security OAuth的默认配置。咱们如今继承:AuthorizationServerConfigurerAdapter,其有针对3个不一样的默认的配置:
AuthorizationServerEndpointsConfigurer:端点配置
ClientDetailsServiceConfigurer:针对于第三方客户端配置;所谓的客户端就是有哪些应用会访问咱们的系统,咱们的认证服务器会决定给哪些第三方应用client去发送令牌。若是这个配置后咱们以前配置文件中配置的clientId和clientSecret将不会起做用了 AuthorizationServerSecurityConfigurer:针对于安全性配置 前端

image.png

以前咱们进行认证时候,发送的/oauth/token请求刚好是发送到TokenEndpoint(他是处理咱们oauth请求的);AuthorizationServerEndpointsConfigurer的配置其实就是针对于TokenEndpoint作一些端点配置。对AuthorizationServerEndpointsConfigurer进行配置时候,须要有对象:AuthenticationManager、UserDetailsService。咱们本身不定义配置AuthorizationServerEndpointsConfigurer的时候,系统会默认去应用找到其对应的属性:AuthenticationManager、UserDetailsService;当咱们想使用本身的逻辑时候,将其属性设置成咱们本身定义的bean 本身覆盖其配置。redis

@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        /**
         * 系统端点配置:endpoints
         * 1.使用咱们本身的受权管理器(AuthenticationManager)和自定义的用户详情服务(UserDetailsService)
         */
         endpoints.authenticationManager(authenticationManager)
                 .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /**
         * 系统第三方客户端配置:
         * 所谓的客户端就是有哪些应用会访问咱们的系统,
         * 咱们的认证服务器会决定给哪些第三方应用client去发送令牌。
         * 若是这个配置后咱们以前配置文件中配置的clientId和clientSecret将不会起做用了
         */
        //目前咱们的应用场景是在咱们的app和咱们的前端;咱们不容许第三方来注册,因此用内存
        clients.inMemory().withClient("yxm")
                .secret("yxmsecret")
                .accessTokenValiditySeconds(7200)//发出去的令牌,有效期是多少? 这里设置为2小时
                .authorizedGrantTypes("refresh_token","password")//针对当前应用客户端:yxm,所能支持的受权模式是哪些?以前设置有4种类加上刷新总共5种:这里只支持配置的:"refresh_token","password"。
                .scopes("all","read","write");//发出去的权限有哪些?以前前端请求携带了scoope,此配置的scope用来指定前端发送scope的值必须在配置的里面或者不携带scope;默认为此处配置的scope
    }
}

而后咱们重启服务:尝试下发送请求的效果. spring

image.png

返回结果中:expires_in是7199,刚好是咱们配置的7200s以内。
scope返回的是咱们client端配置的:"all read write"数据库

{
"access_token": "e9cc6ce9-d241-44d9-9df3-dcfbf5ab1181",
"token_type": "bearer",
"refresh_token": "996949f9-d2ba-4d26-8456-1b678c5b1cf2",
"expires_in": 7199,
"scope": "all read write"
}

针对于client端配置的:authorizedGrantTypes("refresh_token","password")
咱们尝试使用:implicit json

image.png

咱们发送下:scope在客户端不存在的类型:xxx
image.png数组

咱们不发送scope
image.png安全

若是咱们除了给yxm对应的client端受权外,咱们还须要给其余客户端受权。直接在后面加上and拼接便可。
image.png服务器

可是又回到咱们以前说的呢?咱们这个模块应该是通用的模块,只须要配置便可实现接入。
OAuth2ClientPropertiesapp

public class OAuth2ClientProperties {
    //咱们把:ClientDetailsServiceConfigurer相关配置加进来
    private String clientId;
    private String clientSecret;
    private int accessTokenValiditySeconds;

    public String getClientId() {
        return clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public String getClientSecret() {
        return clientSecret;
    }

    public void setClientSecret(String clientSecret) {
        this.clientSecret = clientSecret;
    }

    public int getAccessTokenValiditySeconds() {
        return accessTokenValiditySeconds;
    }

    public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
        this.accessTokenValiditySeconds = accessTokenValiditySeconds;
    }
}

OAuth2Properties:ide

public class OAuth2Properties {
    private OAuth2ClientProperties[] clients = {};//默认空数组  

    public OAuth2ClientProperties[] getClients() {
        return clients;
    }

    public void setClients(OAuth2ClientProperties[] clients) {
        this.clients = clients;
    }
}

而后将其加入到security的配置下去:SecurityProperties

image.png

此时咱们去除demo里面以前的默认的spring security oauth2客户端配置,改成对应的配置。

#security:
#    oauth2:
#      client:
#        clientId: yxm
#        client_secret: yxmsecret

image.png

认证服务器里面修改为配置:

@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private SecurityProperties securityProperties;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        /**
         * 系统端点配置:endpoints
         * 1.使用咱们本身的受权管理器(AuthenticationManager)和自定义的用户详情服务(UserDetailsService)
         */
         endpoints.authenticationManager(authenticationManager)
                 .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /**
         * 系统第三方客户端配置:
         * 所谓的客户端就是有哪些应用会访问咱们的系统,
         * 咱们的认证服务器会决定给哪些第三方应用client去发送令牌。
         * 若是这个配置后咱们以前配置文件中配置的clientId和clientSecret将不会起做用了
         */
        //目前咱们的应用场景是在咱们的app和咱们的前端;咱们不容许第三方来注册,因此用内存
       /* clients.inMemory().withClient("yxm")
                .secret("yxmsecret")
                .accessTokenValiditySeconds(7200)//发出去的令牌,有效期是多少? 这里设置为2小时
                .authorizedGrantTypes("refresh_token","password")//针对当前应用客户端:yxm,所能支持的受权模式是哪些?以前设置有4种类加上刷新总共5种:这里只支持配置的:"refresh_token","password"。
                .scopes("all","read","write")//发出去的权限有哪些?以前前端请求携带了scoope,此配置的scope用来指定前端发送scope的值必须在配置的里面或者不携带scope;默认为此处配置的scope
                .and()
                .withClient("startshineye")
                .secret("startshineyesecret")
                .accessTokenValiditySeconds(3600)
                .authorizedGrantTypes("password")
                .scopes("read");*/
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
        if(ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())) {//判断咱们的配置是否为空
            for (OAuth2ClientProperties client:securityProperties.getOauth2().getClients()){
                builder.withClient(client.getClientId())
                        .secret(client.getClientSecret())
                        .accessTokenValiditySeconds(client.getAccessTokenValiditySeconds())
                        .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                        .scopes("all");
            }
        }
    }
}

咱们如今使用第二次配置:startshineye,并将其accessTokenValiditySeconds配置

image.png

image.png

咱们如今使用第二次配置:startshineye,并将其accessTokenValiditySeconds不配置,返回的json对象中没有令牌:

2.令牌存储

咱们如今的令牌是存储到内存中的,一旦咱们服务重启的话。内存中的令牌就丢失了,因此咱们须要将令牌存储到持久化的数据库或者redis中,因为令牌是访问比较频繁的,因此咱们将其配置到redis中。使用redis你不用去维护表的结构而且过时时间自动设置且性能比数据库快不少。

咱们以前将到过TokenStore,他是用来负责令牌的存取,如今咱们写一个TokenStoreConfig配置类。

@Configuration
public class TokenStoreConfig {
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    public TokenStore redisTokenStore(){
      return new RedisTokenStore(redisConnectionFactory);
    }
}

在咱们的认证服务器上引入自定义TokenStore:

@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private TokenStore redisTokenStore;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        /**
         * 系统端点配置:endpoints
         * 1.使用咱们本身的受权管理器(AuthenticationManager)和自定义的用户详情服务(UserDetailsService)
         */
         endpoints.tokenStore(redisTokenStore)
                 .authenticationManager(authenticationManager)
                 .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /**
         * 系统第三方客户端配置:
         * 所谓的客户端就是有哪些应用会访问咱们的系统,
         * 咱们的认证服务器会决定给哪些第三方应用client去发送令牌。
         * 若是这个配置后咱们以前配置文件中配置的clientId和clientSecret将不会起做用了
         */
        //目前咱们的应用场景是在咱们的app和咱们的前端;咱们不容许第三方来注册,因此用内存
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
        if(ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())) {//判断咱们的配置是否为空
            for (OAuth2ClientProperties client:securityProperties.getOauth2().getClients()){
                builder.withClient(client.getClientId())
                        .secret(client.getClientSecret())
                        .accessTokenValiditySeconds(client.getAccessTokenValiditySeconds())
                        .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                        .scopes("all");
            }
        }
    }
}

测试:

image.png

咱们从redis里面能够查看到对应的信息。
image.png

相关文章
相关标签/搜索