SpringSecurity(18)——OAuth2授权码管理

AuthorizationCodeServices

java 复制代码
public interface AuthorizationCodeServices {

	//为指定的身份验证创建授权代码。
	String createAuthorizationCode(OAuth2Authentication authentication);

	//使用授权码。
	OAuth2Authentication consumeAuthorizationCode(String code)throws InvalidGrantException;
}
java 复制代码
public abstract class RandomValueAuthorizationCodeServices implements AuthorizationCodeServices {

	private RandomValueStringGenerator generator = new RandomValueStringGenerator();

	public String createAuthorizationCode(OAuth2Authentication authentication) {
		String code = generator.generate();
		store(code, authentication);
		return code;
	}

	public OAuth2Authentication consumeAuthorizationCode(String code) throws InvalidGrantException {
		OAuth2Authentication auth = this.remove(code);
		if (auth == null) {throw new InvalidGrantException("Invalid authorization code: " + code);}
		return auth;
	}

    protected abstract void store(String code, OAuth2Authentication authentication);
    protected abstract OAuth2Authentication remove(String code);
}

RandomValueAuthorizationCodeServices为我们指定了授权码的生成方式,同时也开放了授权码的存储和删除。这为我们后面AuthorizationCodeServices的自定义提供了方便,因为我们只要继承RandomValueAuthorizationCodeServices,实现他的store和remove方法就行了。

RandomValueAuthorizationCodeServices有2个子类InMemoryAuthorizationCodeServices和JdbcAuthorizationCodeServices,一个是把授权码存储到内存中,另一个是把授权码存储到数据库中。

JDBC管理授权码

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.3.12.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.3.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.3.12.RELEASE</version>
</dependency>
java 复制代码
@Configuration
public class MyOauth2Config {

    /**
     * druid数据源
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    /**
     * jdbc管理令牌
     */
    @Bean
    public TokenStore jdbcTokenStore() {
        return new JdbcTokenStore(druidDataSource());
    }

    /**
     * 授权码管理策略
     */
    @Bean
    public AuthorizationCodeServices jdbcAuthorizationCodeServices() {
        //使用jdbc方式保存授权码到oauth_code中
        return new JdbcAuthorizationCodeServices(druidDataSource());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
java 复制代码
/**
 * 当前需要使用内存方式存储了用户令牌,应当使用UserDetailsService才行,否则会报错
 */
@Component
public class MyUserDetailService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        return new User("admin", passwordEncoder.encode("123456"),
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin_role"));
    }
}
java 复制代码
@EnableWebSecurity
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailService myUserDetailService;

    /**
     * password密码模式要使用此认证管理器
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 用户类信息
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailService);
    }
}
java 复制代码
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthenticationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private MyUserDetailsService myUserDetailsService;

    @Autowired
    private TokenStore tokenStore;
    
    @Autowired
    private AuthorizationCodeServices jdbcAuthorizationCodeServices;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("test-pc")
                .secret(passwordEncoder.encode("123456"))
                .resourceIds("oauth2-server")
                .authorizedGrantTypes("authorization_code", "password", "implicit", "client_credentials", "refresh_token")
                .scopes("all")
                .autoApprove(false)
                .redirectUris("http://www.baidu.com");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
        endpoints.userDetailsService(myUserDetailsService);
        //令牌管理策略
        endpoints.tokenStore(tokenStore);
        //授权码管理策略,针对授权码模式有效,会将授权码放到oauth_code表,授权后就删除它
        endpoints.authorizationCodeServices(jdbcAuthorizationCodeServices);
    }
}

自定义Redis管理授权码

java 复制代码
@Service
public class RedisAuthorizationCodeServices extends RandomValueAuthorizationCodeServices {

    String AUTH_CODE_PREFIX = "yiyi:code:auth:";

    private final RedisRepository redisRepository;
    private final RedisSerializer<Object> valueSerializer;

    public RedisAuthorizationCodeServices(RedisRepository redisRepository) {
        this.redisRepository = redisRepository;
        this.valueSerializer = RedisSerializer.java();
    }

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 将存储code到redis,并设置过期时间,10分钟
     */
    @Override
    protected void store(String code, OAuth2Authentication authentication) {
        redisRepository.setExpire(redisKey(code), authentication, 10, TimeUnit.MINUTES, valueSerializer);
    }

    @Override
    protected OAuth2Authentication remove(final String code) {
        String codeKey = redisKey(code);
        OAuth2Authentication token = (OAuth2Authentication) redisRepository.get(codeKey, valueSerializer);
        redisRepository.del(codeKey);
        return token;
    }

    /**
     * redis中 code key的前缀
     */
    private String redisKey(String code) {
        return AUTH_CODE_PREFIX + code;
    }
}
java 复制代码
@Configuration
@EnableAuthorizationServer
public class Authorizationservercontig2 extends AuthorizationServerConfigurerAdapter {

    //忽略代码.....
    @Autowired
    private RandomValueAuthorizationCodeServices authorizationCodeServices;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
         //忽略代码....
         endpoints .authorizationCodeServices(authorizationCodeServices)
         //忽略代码....
    }
}
相关推荐
后端小张21 分钟前
【JAVA 进阶】SpringMVC全面解析:从入门到实战的核心知识点梳理
java·开发语言·spring boot·spring·spring cloud·java-ee·springmvc
Lucky小小吴1 小时前
ClamAV扫描速度提升6.5倍:服务器杀毒配置优化实战指南
java·服务器·网络·clamav
handsome_sai7 小时前
【Java 线程池】记录
java
大学生资源网8 小时前
基于springboot的唐史文化管理系统的设计与实现源码(java毕业设计源码+文档)
java·spring boot·课程设计
guslegend8 小时前
SpringSecurity源码剖析
java
roman_日积跬步-终至千里8 小时前
【人工智能导论】02-搜索-高级搜索策略探索篇:从约束满足到博弈搜索
java·前端·人工智能
大学生资源网9 小时前
java毕业设计之儿童福利院管理系统的设计与实现(源码+)
java·开发语言·spring boot·mysql·毕业设计·源码·课程设计
JasmineWr9 小时前
JVM栈空间的使用和优化
java·开发语言
Hello.Reader9 小时前
Flink SQL DELETE 语句批模式行级删除、连接器能力要求与实战避坑(含 Java 示例)
java·sql·flink
爱笑的眼睛119 小时前
从 Seq2Seq 到 Transformer++:深度解构与自构建现代机器翻译核心组件
java·人工智能·python·ai