zoukankan      html  css  js  c++  java
  • 使用Redis作为Spring Security OAuth2的token存储

    写在前边

    本文对Spring Security OAuth2的token使用Redis保存,相比JWT实现的token存储,Redis可以随时吊销access_token,并且Redis响应速度很快,没有加密解密的过程

    本文源代码在redis-token-saved模块中,仓库地址:https://github.com/hellxz/spring-security-oauth2-learn

    这里就不做测试了,仅做简记

    代码层级

    Maven依赖

    	<parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.1.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    	<dependencies>
            
    		<!-- Spring Security OAuth2 -->
            <dependency>
                <groupId>org.springframework.security.oauth</groupId>
                <artifactId>spring-security-oauth2</artifactId>
                <version>2.4.0.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.62</version>
            </dependency>
    	</dependencies>
    

    授权服务器保存token到Redis

    application.yml

    server:
      port: 8080
    
    spring:
      redis:
        host: 127.0.0.1
        port: 6379
        password: 123
        database: 0
    

    AuthorizationConfig

    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
    
        /**
         * redis工厂,默认使用lettue
         */
        @Autowired
        public RedisConnectionFactory redisConnectionFactory;
    
        /**
         * 用户认证管理器
         */
        @Autowired
        public AuthenticationManager authenticationManager;
    
        /**
         * 用户服务
         */
        @Autowired
        public UserDetailsService userDetailsService;
    
        /**
         * 密码加密器
         */
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        /**
         * 授权服务安全配置,主要用于放行客户端访问授权服务接口
         *
         * @param security AuthorizationServerSecurityConfigurer
         * @throws Exception 异常
         */
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            //允许客户端表单提交
            security.allowFormAuthenticationForClients()
                    //客户端校验token访问许可
                    .checkTokenAccess("permitAll()")
                    //客户端token调用许可
                    .tokenKeyAccess("permitAll()");
        }
    
        /**
         * 客户端信息配置,可配置多个客户端,这里可以使用配置文件进行代替
         *
         * @param clients 客户端设置
         * @throws Exception 异常
         */
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient("client-a")
                    .secret(passwordEncoder.encode("client-a-secret"))
                    .redirectUris("http://localhost:9001/callback")
                    //支持 授权码、密码两种授权模式,支持刷新token功能
                    .authorizedGrantTypes("authorization_code", "password", "refresh_token");
        }
    
        /**
         * 配置端点
         *
         * @param endpoints 端点
         * @throws Exception 异常
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            //配置认证管理器
            endpoints.authenticationManager(authenticationManager)
                    //配置用户服务
                    .userDetailsService(userDetailsService)
                    //配置token存储的服务与位置
                    .tokenServices(tokenService())
                    .tokenStore(tokenStore());
        }
    
        @Bean
        public TokenStore tokenStore() {
            //使用redis存储token
            RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
            //设置redis token存储中的前缀
            redisTokenStore.setPrefix("auth-token:");
            return redisTokenStore;
        }
    
        @Bean
        public DefaultTokenServices tokenService() {
            DefaultTokenServices tokenServices = new DefaultTokenServices();
            //配置token存储
            tokenServices.setTokenStore(tokenStore());
            //开启支持refresh_token,此处如果之前没有配置,启动服务后再配置重启服务,可能会导致不返回token的问题,解决方式:清除redis对应token存储
            tokenServices.setSupportRefreshToken(true);
            //复用refresh_token
            tokenServices.setReuseRefreshToken(true);
            //token有效期,设置12小时
            tokenServices.setAccessTokenValiditySeconds(12 * 60 * 60);
            //refresh_token有效期,设置一周
            tokenServices.setRefreshTokenValiditySeconds(7 * 24 * 60 * 60);
            return tokenServices;
        }
    }
    
    

    SecurityConfig

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        /**
         * 配置认证管理器信息,这里可以使用UserDetailsService实现类来提供用户信息,或Provider+UserDetailsService
         *
         * @param auth 认证管理器配置
         * @throws Exception 异常
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            //@formatter:off
            auth.inMemoryAuthentication()
                    .withUser("hellxz")
                    .password(passwordEncoder().encode("test"))
                    .authorities(AuthorityUtils.createAuthorityList("all"));
            //@formatter:on
        }
    
        /**
         * 配置http访问控制
         *
         * @param http http安全配置
         * @throws Exception 异常
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    //放行options方法请求
                    .antMatchers(HttpMethod.OPTIONS).permitAll()
                    .anyRequest().authenticated()
                    .and()
                    .csrf().disable();
        }
    
        @Bean
        public AuthenticationManager authenticationManager() throws Exception {
            return super.authenticationManager();
        }
    
        @Bean
        public UserDetailsService userDetailsService() {
            return super.userDetailsService();
        }
    }
    

    启动类

    /**
     * redis作为token存储的授权server
     */
    @SpringBootApplication
    public class RedisAuthorizationServer {
    
        public static void main(String[] args) {
            SpringApplication.run(RedisAuthorizationServer.class, args);
        }
    }
    

    资源服务器访问Redis校验token配置

    application.yml

    server:
      port: 8081
    
    spring:
      redis:
        host: 127.0.0.1
        port: 6379
        password: 123
        database: 0
    

    ResourceServerConfig

    @Configuration
    @EnableResourceServer
    public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    
        @Autowired
        private RedisConnectionFactory redisConnectionFactory;
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            //无状态
            resources.stateless(true);
            //设置token存储
            resources.tokenStore(tokenStore());
        }
    
        /**
         * 设置token存储,这一点配置要与授权服务器相一致
         */
        @Bean
        public RedisTokenStore tokenStore(){
            RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
            redisTokenStore.setPrefix("auth-token:");
            return redisTokenStore;
        }
    }
    

    SecurityConfig

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                    .httpBasic();
        }
    }
    

    ResourceController

    @RestController
    public class ResourceController {
        private static final Logger log = LoggerFactory.getLogger(ResourceController.class);
    
        @GetMapping("/user/{username}")
        public UserVO user(@PathVariable String username){
            log.info("{}", SecurityContextHolder.getContext().getAuthentication());
            return new UserVO(username, username + "@foxmail.com");
        }
    }
    

    UserVO

    package com.github.hellxz.oauth2.web.vo;
    
    public class UserVO {
        private String username;
        private String email;
    
        public UserVO(String username, String email) {
            this.username = username;
            this.email = email;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    }
    

    启动类

    /**
     * 使用redis作为token存储的资源服务器,这里不使用调用授权服务器的方式去校验资源,只需要从redis中取token进行判断即可
     */
    @SpringBootApplication
    public class RedisResourceServer {
        public static void main(String[] args) {
            SpringApplication.run(RedisResourceServer.class, args);
        }
    }
    
  • 相关阅读:
    P5049 旅行(数据加强版)(基环树)
    P5024 保卫王国(动态dp/整体dp/倍增dp)
    CF891C Envy(离线/在线+可撤销并查集/并查集/LCT)
    CF1217题解
    CF1215题解
    浅谈bitset
    CF1214题解
    CF1213F Unstable String Sort(差分)
    C++创建和使用动态链接库
    交叉编译openssl1.1.1a
  • 原文地址:https://www.cnblogs.com/hellxz/p/12044482.html
Copyright © 2011-2022 走看看