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管理令牌*/@Beanpublic TokenStore jdbcTokenStore() {return new JdbcTokenStore(druidDataSource());}/*** 授权码管理策略*/@Beanpublic AuthorizationCodeServices jdbcAuthorizationCodeServices() {//使用jdbc方式保存授权码到oauth_code中return new JdbcAuthorizationCodeServices(druidDataSource());}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}
/*** 当前需要使用内存方式存储了用户令牌,应当使用UserDetailsService才行,否则会报错*/
@Component
public class MyUserDetailService implements UserDetailsService {@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {return new User("admin", passwordEncoder.encode("123456"),AuthorityUtils.commaSeparatedStringToAuthorityList("admin_role"));}
}
@EnableWebSecurity
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate MyUserDetailService myUserDetailService;/*** password密码模式要使用此认证管理器*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/*** 用户类信息*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(myUserDetailService);}
}
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthenticationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate MyUserDetailsService myUserDetailsService;@Autowiredprivate TokenStore tokenStore;@Autowiredprivate AuthorizationCodeServices jdbcAuthorizationCodeServices;@Overridepublic 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");}@Overridepublic 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();}@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 将存储code到redis,并设置过期时间,10分钟*/@Overrideprotected void store(String code, OAuth2Authentication authentication) {redisRepository.setExpire(redisKey(code), authentication, 10, TimeUnit.MINUTES, valueSerializer);}@Overrideprotected 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 {//忽略代码.....@Autowiredprivate RandomValueAuthorizationCodeServices authorizationCodeServices;@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {//忽略代码....endpoints .authorizationCodeServices(authorizationCodeServices)//忽略代码....}
}