2023-06-18
原文作者:代码有毒 mrcode 原文地址:https://mrcode.blog.csdn.net/article/details/81502532

令牌配置

接下来的内容是:

  • 基本的Token参数配置
  • 使用jwt替换默认的token
  • 扩展和解析jwt的信息

token 的处理在认证服务器处理的。之前已经配置了资源服务器,现在来自定义认证服务器

spirng boot 2 的自动配置文件和1.5的不一样
直接跟着视频走是不会成功的,原因如下

OAuth2AuthorizationServerConfiguration 类是 @EnableAuthorizationServer 的自动配置类;
如果我们 继承了 AuthorizationServerConfigurerAdapter,那么该类将不会被初始化,认证服务器将不能正常工作
(看源码中的条件注解声明得知)

这是根据自动配置类简化而来的配置。正常使用

    package cn.mrcode.imooc.springsecurity.securityapp;
    
    /**
     * ${desc}
     * @author zhuqiang
     * @version 1.0.1 2018/8/7 10:52
     * @date 2018/8/7 10:52
     * @since 1.0
     */
    @Configuration
    @EnableAuthorizationServer
    public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
        private final AuthenticationManager authenticationManager;
    //    @Autowired
    //    private PasswordEncoder passwordEncoder;
    
        public MyAuthorizationServerConfig(
                AuthenticationConfiguration authenticationConfiguration) throws Exception {
            this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
        }
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient("myid")
                    .secret("myid")
                    .redirectUris("http://example.com", "http://ora.com")
                    .and()
                    .withClient("myid2")
                    .secret("myid2")
                    .redirectUris("http://example.com", "localhost:8080")
                    .authorizedGrantTypes("refresh_token", "password")
                    .accessTokenValiditySeconds(7200)
                    .scopes("all", "read", "write");
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(this.authenticationManager);
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            // 这里使用什么密码需要 根据上面配置client信息里面的密码类型决定
            // 目前上面配置的是无加密的密码
            security.passwordEncoder(NoOpPasswordEncoder.getInstance());
        }
    }

抽成配置

下面记录下一些注意的地方

yml中数组和对象嵌套的写法如下:

    imooc:
      security:
        oauth2:
          clients:
            -
              clientId: myid
              clientSecret: myid
              redirectUris:
                - "http://example.com"
                - "http://ora.com"
              accessTokenValiditySeconds: 0
            -
              clientId: myid2
              clientSecret: myid2
              authorizedGrantTypes: ["refresh_token", "password"]
              redirectUris:
                - "http://example.com"
                - "localhost:8080"
              scopes: ["all", "read", "write"]
              accessTokenValiditySeconds: 7200

配置类对应,注意看下面的数组。都是默认为空数组,这样不会导致代码中npe

    public class OAuth2Properties {
        private OAuth2ClientProperties[] clients = {};
    
    public class OAuth2ClientProperties {
        private String clientId;
        private String clientSecret;
        private String[] authorizedGrantTypes = {};
        private String[] redirectUris = {}; // 信任的回调域
        private String[] scopes = {};
        private int accessTokenValiditySeconds; // token有效期

配置使用的地方

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        InMemoryClientDetailsServiceBuilder inMemory = clients.inMemory();
        OAuth2ClientProperties[] clientsInCustom = securityProperties.getOauth2().getClients();
        for (OAuth2ClientProperties p : clientsInCustom) {
            inMemory.withClient(p.getClientId())
                    .secret(p.getClientSecret())
                    .redirectUris(p.getRedirectUris())
                    .authorizedGrantTypes(p.getAuthorizedGrantTypes())
                    .accessTokenValiditySeconds(p.getAccessTokenValiditySeconds())
                    .scopes(p.getScopes());
        }
        logger.info(Arrays.toString(clientsInCustom));
    }

tokenStore 使用redis来存储

上面的配置都是使用的内存来存储令牌信息;令牌的存储和获取比较频繁,为了能持久化。使用redis

在认证服务配置类中配置tokenStore即可

    cn.mrcode.imooc.springsecurity.securityapp.MyAuthorizationServerConfig
    
    @Autowired(required = false)
    public TokenStore tokenStore;
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(this.authenticationManager);
        endpoints.tokenStore(tokenStore);
    }

TokenStore 需要使用的地方初始化对象,也就是app中

    package cn.mrcode.imooc.springsecurity.securityapp;
    
    import cn.mrcode.imooc.springsecurity.securitycore.MyRedisTokenStore;
    
    @Configuration
    public class TokenStoreConfig {
        @Autowired
        private RedisConnectionFactory redisConnectionFactory;
    
        @Bean
        public TokenStore tokenStore() {
            return new MyRedisTokenStore(redisConnectionFactory);
        }
    }

注意在spring boot 2.0.4 中;使用默认的 RedisTokenStore 在存储的时候会出现异常;

解决方案:把RedisTokenStore的代码完全copy一份,然后把 storeAccessToken 方法中调用
conn.set的代码全部缓存 conn.stringCommands().set

    @Override
       public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
           byte[] serializedAccessToken = serialize(token);
           byte[] serializedAuth = serialize(authentication);
           byte[] accessKey = serializeKey(ACCESS + token.getValue());
           byte[] authKey = serializeKey(AUTH + token.getValue());
           byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication));
           byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication));
           byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
    
           RedisConnection conn = getConnection();
           try {
               conn.openPipeline();
               // 这里 set的时候,子类都不支持存储自己数组
               conn.stringCommands().set(accessKey, serializedAccessToken);

一篇不错的redis的配置文章:https://majing.io/posts/10000020931206
切换到jedis中还是会出现一样的错误,所以不用测试jedis了;就是依赖包不兼容的问题,

阅读全文