AuthorizationCodeServices
public interface AuthorizationCodeServices {
//为指定的身份验证创建授权代码。
String createAuthorizationCode(OAuth2Authentication authentication);
//使用授权码。
OAuth2Authentication consumeAuthorizationCode(String code)throws InvalidGrantException;
}
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管理授权码
<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>
@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();
}
}
/**
* 当前需要使用内存方式存储了用户令牌,应当使用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"));
}
}
@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);
}
}
@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管理授权码
@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;
}
}
@Configuration
@EnableAuthorizationServer
public class Authorizationservercontig2 extends AuthorizationServerConfigurerAdapter {
//忽略代码.....
@Autowired
private RandomValueAuthorizationCodeServices authorizationCodeServices;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//忽略代码....
endpoints .authorizationCodeServices(authorizationCodeServices)
//忽略代码....
}
}