使用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);
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM