认证中心功能主要包括:1.发放token
2.验证token
3.提供第三方登录 登出的页面
SpringSecurityConfig
与前文的配置相同 主要提供认证与授权功能
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 失败处理
*/
@Autowired
private AuthenticationFailureHandler customAuthenticationFailureHandler;
/**
* 成功处理
*/
@Autowired
private AuthenticationSuccessHandler customAuthenticationSuccessHandler;
/**
* 加密方法
*/
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService customUserDetailsService;
/**
* 验证码过滤器
*/
@Autowired
private ImageCodeValidateFilter imageCodeValidateFilter;
/**
* 连接数据源
*/
@Autowired
DataSource dataSource;
/**
* 验证用户是否正确
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
/**
* password 模式需要此bean 否则报错
*
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 记住我功能使用
*
* @return
*/
@Bean
public JdbcTokenRepositoryImpl jdbcTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
/**
* 放行静态资源
*
* @param web
*/
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/js/**", "/css/**");
}
/**
* 为了解决退出重新登录问题
*
* @return
*/
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 验证码过滤器
http.addFilterBefore(imageCodeValidateFilter, UsernamePasswordAuthenticationFilter.class)
// 跳转前台的地址
.formLogin().loginPage("/loginPage")
// 登录调用的接口地址
.loginProcessingUrl("/login").successHandler(customAuthenticationSuccessHandler)
// 无权限允许访问 login等这些请求时还没有token
.and().authorizeRequests()
.antMatchers("/login", "/loginPage", "/code/image", "/logout/expirePage", "/logout/page",
"/oauth/logoutSession", "/oauth/customLogout")
.permitAll().anyRequest().authenticated().and().rememberMe() // 记住功能配置
.tokenRepository(jdbcTokenRepository()) // 保存登录信息
.tokenValiditySeconds(60 * 30 * 30) // 记住我有效时长
// 登出配置
.and().csrf().disable();
}
}
AuthorizationServerConfig
oauth2的核心配置类 可以说 SpringSecurityConfig与AuthorizationServerConfig配置都了解了 那么就算了解oauth2+spring security
小伙伴可以参考 去掉不使用的配置项 如果不想使用自定义的类 可以查看自定义实现类实现的接口 使用默认类
@Configuration
@EnableAuthorizationServer // 开启了认证服务器
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
/**
* 密码加密
*/
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 连接数据源
*/
@Autowired
private DataSource dataSource;
/**
* password 要这个 AuthenticationManager 实例 否则报错
*/
@Autowired
private AuthenticationManager authenticationManager;
/**
* 查询用户功能
*/
@Autowired
private UserDetailsService customUserDetailsService;
/**
* token管理方式,在TokenConfig类中已对添加到容器中了
*/
@Autowired
private TokenStore tokenStore;
/**
* 使用数据库管理客户端 也就是上文介绍的 oauth_client_details表
* @return
*/
@Bean
public ClientDetailsService jdbcClientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
/**
* 当无权限时调用 返回前台json
*/
@Autowired
private CustomAccessDeniedHandler customAccessDeniedHandlerConfig;
/**
* 配置被允许访问此认证服务器的客户端信息 oauth_client_details表
* 1.内存方式
* 2. 数据库方式
*
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// jdbc管理客户端
clients.withClientDetails(jdbcClientDetailsService());
}
/**
* 授权码管理策略 可以保存授权码到数据库 并没有太大软用
*
* @return
*/
@Bean
public AuthorizationCodeServices jdbcAuthorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
/**
* 指定由那些字符生成token 可以参考上文
* @return
*/
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
/**
* 关于认证服务器端点配置
*
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// password 要这个 AuthenticationManager 实例
endpoints.authenticationManager(authenticationManager);
// 刷新令牌时需要使用
endpoints.userDetailsService(customUserDetailsService);
// 自定义令牌加强
endpoints.tokenServices(tokenService());
// 授权码管理策略 会产生的授权码放到 oauth_code 表中,如果这个授权码已经使用了,则对应这个表中的数据就会被删除
endpoints.authorizationCodeServices(jdbcAuthorizationCodeServices());
// 自定义授权页面
endpoints.pathMapping("/oauth/confirm_access", "/custom/confirm_access");
}
@Bean
public CustomDefaultTokenServices tokenService() {
// 自定义token生成方式 前文自定义了每次登陆都生成新token 可以查看上文
CustomDefaultTokenServices tokenServices = new CustomDefaultTokenServices();
// token持久化容器
tokenServices.setTokenStore(tokenStore);
// 指定token生成字符串
tokenServices.setTokenEnhancer(tokenEnhancer());
// access_token 的有效时长 (秒), 默认 12 小时
tokenServices.setAccessTokenValiditySeconds(60 * 60 * 2);
return tokenServices;
}
/**
* 令牌端点的安全配置
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//如果没有权限访问 返回前台json
security.accessDeniedHandler(customAccessDeniedHandlerConfig);
// 认证后可访问 /oauth/token_key , 默认拒绝访问
security.tokenKeyAccess("permitAll()");
// 认证后可访问 /oauth/check_token , 默认拒绝访问
security.checkTokenAccess("isAuthenticated()");
}
}
以下内容基本在前文已经讲过 没看过的小伙伴可以到前文看看
CustomAuthenticationFailureHandler ,CustomAccessDeniedHandler
与前台交互为json 不多说
@Component("customAuthenticationFailureHandler")
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
/**
* @param exception 认证失败时抛出异常
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
String referer = request.getHeader("Referer");
logger.info("referer:" + referer);
// 如果下面有值,则认为是多端登录,直接返回一个登录地址
Object toAuthentication = request.getAttribute("toAuthentication");
String lastUrl = toAuthentication != null ? LOGIN_PAGE : StringUtils.substringBefore(referer, "?");
logger.info("上一次请求的路径 :" + lastUrl);
super.setDefaultFailureUrl(lastUrl + "?error");
super.onAuthenticationFailure(request, response, exception);
}
}
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setStatus(HttpStatus.OK.value());
response.setHeader("Content-Type", "application/json;charset=UTF-8");
try {
Result result = new Result(403, "权限不足");
response.getWriter().write(new ObjectMapper().writeValueAsString(result));
} catch (IOException e) {
e.printStackTrace();
}
}
}
TokenConfig
token使用redis保存 使用自定义的tokenStore 可以参考上文
@Configuration
public class TokenConfig {
/**
* 使用redis存储
*/
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Bean
public TokenStore tokenStore() {
return new CustomRedisTokenStore(redisConnectionFactory);
}
}
SpringSecurityBean
密码加密解密使用 不多说
@Configuration
public class SpringSecurityBean {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
ReloadMessageConfig
自定义返回异常为中文 可以参考上文
@Configuration
public class ReloadMessageConfig {
/**
* 加载中文的认证提示信息
*
* @return
*/
@Bean
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
// .properties 不要加到后面
messageSource.setBasename("classpath:messages_zh_CN");
return messageSource;
}
}
CustomTokenEnhancer,CustomDefaultTokenServices
自定义token的方法和生成 可以参考上文
public class CustomTokenEnhancer implements TokenEnhancer {
@Autowired
Utils utils;
/**
* @Description 重新定义令牌token
* @Date 2019/7/9 19:56
* @Version 1.0
*/
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
if (accessToken instanceof DefaultOAuth2AccessToken) {
DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken)accessToken;
Map<String, Object> additionalInformation = Maps.newHashMap();
additionalInformation.put("client_id", authentication.getOAuth2Request().getClientId());
additionalInformation.put("client_Ip", utils.getIpAddr());
// 获取用户名
SysUser sysUser = (SysUser)authentication.getPrincipal();
additionalInformation.put("user_name", sysUser.getUsername());
additionalInformation.put("user_id", sysUser.getId());
// 获取浏览器名称
additionalInformation.put("web_name", utils.getWebName());
token.setAdditionalInformation(additionalInformation);
return token;
}
return accessToken;
}
}
public class CustomDefaultTokenServices
implements AuthorizationServerTokenServices, ResourceServerTokenServices, ConsumerTokenServices, InitializingBean {
private int refreshTokenValiditySeconds = 2592000;
private int accessTokenValiditySeconds = 43200;
private boolean supportRefreshToken = false;
private boolean reuseRefreshToken = true;
private TokenStore tokenStore;
private ClientDetailsService clientDetailsService;
private TokenEnhancer accessTokenEnhancer;
private AuthenticationManager authenticationManager;
public CustomDefaultTokenServices() {}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.tokenStore, "tokenStore must be set");
}
@Override
@Transactional
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
OAuth2AccessToken existingAccessToken = this.tokenStore.getAccessToken(authentication);
OAuth2RefreshToken refreshToken = null;
// 每一次都重新生成token
if (refreshToken == null) {
refreshToken = this.createRefreshToken(authentication);
} else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken)refreshToken;
if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
refreshToken = this.createRefreshToken(authentication);
}
}
OAuth2AccessToken accessToken = this.createAccessToken(authentication, refreshToken);
this.tokenStore.storeAccessToken(accessToken, authentication);
refreshToken = accessToken.getRefreshToken();
if (refreshToken != null) {
this.tokenStore.storeRefreshToken(refreshToken, authentication);
}
return accessToken;
}
@Override
@Transactional(noRollbackFor = {InvalidTokenException.class, InvalidGrantException.class})
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
throws AuthenticationException {
if (!this.supportRefreshToken) {
throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
} else {
OAuth2RefreshToken refreshToken = this.tokenStore.readRefreshToken(refreshTokenValue);
if (refreshToken == null) {
throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
} else {
OAuth2Authentication authentication = this.tokenStore.readAuthenticationForRefreshToken(refreshToken);
if (this.authenticationManager != null && !authentication.isClientOnly()) {
Authentication user = new PreAuthenticatedAuthenticationToken(
authentication.getUserAuthentication(), "", authentication.getAuthorities());
user = this.authenticationManager.authenticate(user);
Object details = authentication.getDetails();
authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
authentication.setDetails(details);
}
String clientId = authentication.getOAuth2Request().getClientId();
if (clientId != null && clientId.equals(tokenRequest.getClientId())) {
this.tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);
if (this.isExpired(refreshToken)) {
this.tokenStore.removeRefreshToken(refreshToken);
throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);
} else {
authentication = this.createRefreshedAuthentication(authentication, tokenRequest);
if (!this.reuseRefreshToken) {
this.tokenStore.removeRefreshToken(refreshToken);
refreshToken = this.createRefreshToken(authentication);
}
OAuth2AccessToken accessToken = this.createAccessToken(authentication, refreshToken);
this.tokenStore.storeAccessToken(accessToken, authentication);
if (!this.reuseRefreshToken) {
this.tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
}
return accessToken;
}
} else {
throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
}
}
}
}
@Override
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
return this.tokenStore.getAccessToken(authentication);
}
private OAuth2Authentication createRefreshedAuthentication(OAuth2Authentication authentication,
TokenRequest request) {
Set<String> scope = request.getScope();
OAuth2Request clientAuth = authentication.getOAuth2Request().refresh(request);
if (scope != null && !scope.isEmpty()) {
Set<String> originalScope = clientAuth.getScope();
if (originalScope == null || !originalScope.containsAll(scope)) {
throw new InvalidScopeException(
"Unable to narrow the scope of the client authentication to " + scope + ".", originalScope);
}
clientAuth = clientAuth.narrowScope(scope);
}
OAuth2Authentication narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication());
return narrowed;
}
protected boolean isExpired(OAuth2RefreshToken refreshToken) {
if (!(refreshToken instanceof ExpiringOAuth2RefreshToken)) {
return false;
} else {
ExpiringOAuth2RefreshToken expiringToken = (ExpiringOAuth2RefreshToken)refreshToken;
return expiringToken.getExpiration() == null
|| System.currentTimeMillis() > expiringToken.getExpiration().getTime();
}
}
@Override
public OAuth2AccessToken readAccessToken(String accessToken) {
return this.tokenStore.readAccessToken(accessToken);
}
@Override
public OAuth2Authentication loadAuthentication(String accessTokenValue)
throws AuthenticationException, InvalidTokenException {
OAuth2AccessToken accessToken = this.tokenStore.readAccessToken(accessTokenValue);
if (accessToken == null) {
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
} else if (accessToken.isExpired()) {
this.tokenStore.removeAccessToken(accessToken);
throw new InvalidTokenException("Access token expired: " + accessTokenValue);
} else {
OAuth2Authentication result = this.tokenStore.readAuthentication(accessToken);
if (result == null) {
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
} else {
if (this.clientDetailsService != null) {
String clientId = result.getOAuth2Request().getClientId();
try {
this.clientDetailsService.loadClientByClientId(clientId);
} catch (ClientRegistrationException var6) {
throw new InvalidTokenException("Client not valid: " + clientId, var6);
}
}
return result;
}
}
}
public String getClientId(String tokenValue) {
OAuth2Authentication authentication = this.tokenStore.readAuthentication(tokenValue);
if (authentication == null) {
throw new InvalidTokenException("Invalid access token: " + tokenValue);
} else {
OAuth2Request clientAuth = authentication.getOAuth2Request();
if (clientAuth == null) {
throw new InvalidTokenException("Invalid access token (no client id): " + tokenValue);
} else {
return clientAuth.getClientId();
}
}
}
@Override
public boolean revokeToken(String tokenValue) {
OAuth2AccessToken accessToken = this.tokenStore.readAccessToken(tokenValue);
if (accessToken == null) {
return false;
} else {
if (accessToken.getRefreshToken() != null) {
this.tokenStore.removeRefreshToken(accessToken.getRefreshToken());
}
this.tokenStore.removeAccessToken(accessToken);
return true;
}
}
private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
if (!this.isSupportRefreshToken(authentication.getOAuth2Request())) {
return null;
} else {
int validitySeconds = this.getRefreshTokenValiditySeconds(authentication.getOAuth2Request());
String value = UUID.randomUUID().toString();
return (OAuth2RefreshToken)(validitySeconds > 0
? new DefaultExpiringOAuth2RefreshToken(value,
new Date(System.currentTimeMillis() + (long)validitySeconds * 1000L))
: new DefaultOAuth2RefreshToken(value));
}
}
private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
int validitySeconds = this.getAccessTokenValiditySeconds(authentication.getOAuth2Request());
if (validitySeconds > 0) {
token.setExpiration(new Date(System.currentTimeMillis() + (long)validitySeconds * 1000L));
}
token.setRefreshToken(refreshToken);
token.setScope(authentication.getOAuth2Request().getScope());
return (OAuth2AccessToken)(this.accessTokenEnhancer != null
? this.accessTokenEnhancer.enhance(token, authentication) : token);
}
protected int getAccessTokenValiditySeconds(OAuth2Request clientAuth) {
if (this.clientDetailsService != null) {
ClientDetails client = this.clientDetailsService.loadClientByClientId(clientAuth.getClientId());
Integer validity = client.getAccessTokenValiditySeconds();
if (validity != null) {
return validity;
}
}
return this.accessTokenValiditySeconds;
}
protected int getRefreshTokenValiditySeconds(OAuth2Request clientAuth) {
if (this.clientDetailsService != null) {
ClientDetails client = this.clientDetailsService.loadClientByClientId(clientAuth.getClientId());
Integer validity = client.getRefreshTokenValiditySeconds();
if (validity != null) {
return validity;
}
}
return this.refreshTokenValiditySeconds;
}
protected boolean isSupportRefreshToken(OAuth2Request clientAuth) {
if (this.clientDetailsService != null) {
ClientDetails client = this.clientDetailsService.loadClientByClientId(clientAuth.getClientId());
return client.getAuthorizedGrantTypes().contains("refresh_token");
} else {
return this.supportRefreshToken;
}
}
public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) {
this.accessTokenEnhancer = accessTokenEnhancer;
}
public void setRefreshTokenValiditySeconds(int refreshTokenValiditySeconds) {
this.refreshTokenValiditySeconds = refreshTokenValiditySeconds;
}
public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
this.accessTokenValiditySeconds = accessTokenValiditySeconds;
}
public void setSupportRefreshToken(boolean supportRefreshToken) {
this.supportRefreshToken = supportRefreshToken;
}
public void setReuseRefreshToken(boolean reuseRefreshToken) {
this.reuseRefreshToken = reuseRefreshToken;
}
public void setTokenStore(TokenStore tokenStore) {
this.tokenStore = tokenStore;
}
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public void setClientDetailsService(ClientDetailsService clientDetailsService) {
this.clientDetailsService = clientDetailsService;
}
}
CustomRedisTokenStore
token续期使用 可以参考上文
public class CustomRedisTokenStore implements TokenStore {
@Autowired
Utils utils;
Logger logger = LoggerFactory.getLogger(getClass());
private final RedisConnectionFactory connectionFactory;
private static final String ACCESS = "access:";
private static final String AUTH_TO_ACCESS = "auth_to_access:";
private static final String AUTH = "auth:";
private static final String REFRESH_AUTH = "refresh_auth:";
private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
private static final String REFRESH = "refresh:";
private static final String REFRESH_TO_ACCESS = "refresh_to_access:";
private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";
private static final String UNAME_TO_ACCESS = "uname_to_access:";
private static final boolean springDataRedis_2_0 =
ClassUtils.isPresent("org.springframework.data.redis.connection.RedisStandaloneConfiguration",
RedisTokenStore.class.getClassLoader());
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
private String prefix = "";
private Method redisConnectionSet_2_0;
public CustomRedisTokenStore(RedisConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
if (springDataRedis_2_0) {
this.loadRedisConnectionMethods_2_0();
}
}
public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
this.authenticationKeyGenerator = authenticationKeyGenerator;
}
public void setSerializationStrategy(RedisTokenStoreSerializationStrategy serializationStrategy) {
this.serializationStrategy = serializationStrategy;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
private void loadRedisConnectionMethods_2_0() {
this.redisConnectionSet_2_0 =
ReflectionUtils.findMethod(RedisConnection.class, "set", new Class[] {byte[].class, byte[].class});
}
private RedisConnection getConnection() {
return this.connectionFactory.getConnection();
}
private byte[] serialize(Object object) {
return this.serializationStrategy.serialize(object);
}
private byte[] serializeKey(String object) {
return this.serialize(this.prefix + object);
}
private OAuth2AccessToken deserializeAccessToken(byte[] bytes) {
return (OAuth2AccessToken)this.serializationStrategy.deserialize(bytes, OAuth2AccessToken.class);
}
private OAuth2Authentication deserializeAuthentication(byte[] bytes) {
return (OAuth2Authentication)this.serializationStrategy.deserialize(bytes, OAuth2Authentication.class);
}
private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) {
return (OAuth2RefreshToken)this.serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class);
}
private byte[] serialize(String string) {
return this.serializationStrategy.serialize(string);
}
private String deserializeString(byte[] bytes) {
return this.serializationStrategy.deserializeString(bytes);
}
@Override
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
String key = this.authenticationKeyGenerator.extractKey(authentication);
byte[] serializedKey = this.serializeKey("auth_to_access:" + key);
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(serializedKey);
} finally {
conn.close();
}
OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
if (accessToken != null) {
OAuth2Authentication storedAuthentication = this.readAuthentication(accessToken.getValue());
if (storedAuthentication == null
|| !key.equals(this.authenticationKeyGenerator.extractKey(storedAuthentication))) {
this.storeAccessToken(accessToken, authentication);
}
}
return accessToken;
}
/**
* 重写方法 为token续期
*
* @param token
* @return
*/
@Override
public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
OAuth2Authentication result = this.readAuthentication(token.getValue());
if (result != null) {
// 如果token失效更新token时间
DefaultOAuth2AccessToken oAuth2AccessToken = (DefaultOAuth2AccessToken)token;
// 获取ip和用户信息
String ip = String.valueOf(oAuth2AccessToken.getAdditionalInformation().get("client_Ip"));
String user_name = String.valueOf(oAuth2AccessToken.getAdditionalInformation().get("user_name"));
String web_name = String.valueOf(oAuth2AccessToken.getAdditionalInformation().get("web_name"));
// 获取所有token
List<OAuth2AccessToken> allToken = utils.getAllToken();
for (OAuth2AccessToken loadToken : allToken) {
if (ip.equals(loadToken.getAdditionalInformation().get("client_Ip"))
&& user_name.equals(loadToken.getAdditionalInformation().get("user_name"))
&& web_name.equals(loadToken.getAdditionalInformation().get("web_name"))) {
// 这些token需要续期
int expiresIn = loadToken.getExpiresIn();
int second = 60 * 60 * 2;
if (expiresIn < second) {
oAuth2AccessToken.setExpiration(new Date(System.currentTimeMillis() + (second * 1000L)));
}
// 将重新设置过的token过期时间设置到redis 此时会覆盖原本的过期时间
storeAccessToken(token, result);
}
}
logger.debug("token过期已续期");
}
return result;
}
@Override
public OAuth2Authentication readAuthentication(String token) {
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(this.serializeKey("auth:" + token));
} finally {
conn.close();
}
OAuth2Authentication var4 = this.deserializeAuthentication(bytes);
return var4;
}
@Override
public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
return this.readAuthenticationForRefreshToken(token.getValue());
}
public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
RedisConnection conn = this.getConnection();
OAuth2Authentication var5;
try {
byte[] bytes = conn.get(this.serializeKey("refresh_auth:" + token));
OAuth2Authentication auth = this.deserializeAuthentication(bytes);
var5 = auth;
} finally {
conn.close();
}
return var5;
}
@Override
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
RequestAttributes requsetAttributes = RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes)requsetAttributes).getRequest();
byte[] serializedAccessToken = this.serialize((Object)token);
byte[] serializedAuth = this.serialize((Object)authentication);
byte[] accessKey = this.serializeKey("access:" + token.getValue());
byte[] authKey = this.serializeKey("auth:" + token.getValue());
byte[] authToAccessKey =
this.serializeKey("auth_to_access:" + this.authenticationKeyGenerator.extractKey(authentication));
byte[] approvalKey = this.serializeKey("uname_to_access:" + getApprovalKey(authentication));
byte[] clientId = this.serializeKey("client_id_to_access:" + authentication.getOAuth2Request().getClientId());
RedisConnection conn = this.getConnection();
try {
conn.openPipeline();
if (springDataRedis_2_0) {
try {
this.redisConnectionSet_2_0.invoke(conn, accessKey, serializedAccessToken);
this.redisConnectionSet_2_0.invoke(conn, authKey, serializedAuth);
this.redisConnectionSet_2_0.invoke(conn, authToAccessKey, serializedAccessToken);
} catch (Exception var24) {
throw new RuntimeException(var24);
}
} else {
conn.set(accessKey, serializedAccessToken);
conn.set(authKey, serializedAuth);
conn.set(authToAccessKey, serializedAccessToken);
}
if (!authentication.isClientOnly()) {
conn.sAdd(approvalKey, new byte[][] {serializedAccessToken});
}
conn.sAdd(clientId, new byte[][] {serializedAccessToken});
if (token.getExpiration() != null) {
int seconds = token.getExpiresIn();
conn.expire(accessKey, (long)seconds);
conn.expire(authKey, (long)seconds);
conn.expire(authToAccessKey, (long)seconds);
conn.expire(clientId, (long)seconds);
conn.expire(approvalKey, (long)seconds);
}
OAuth2RefreshToken refreshToken = token.getRefreshToken();
if (refreshToken != null && refreshToken.getValue() != null) {
byte[] refresh = this.serialize(token.getRefreshToken().getValue());
byte[] auth = this.serialize(token.getValue());
byte[] refreshToAccessKey =
this.serializeKey("refresh_to_access:" + token.getRefreshToken().getValue());
byte[] accessToRefreshKey = this.serializeKey("access_to_refresh:" + token.getValue());
if (springDataRedis_2_0) {
try {
this.redisConnectionSet_2_0.invoke(conn, refreshToAccessKey, auth);
this.redisConnectionSet_2_0.invoke(conn, accessToRefreshKey, refresh);
} catch (Exception var23) {
throw new RuntimeException(var23);
}
} else {
conn.set(refreshToAccessKey, auth);
conn.set(accessToRefreshKey, refresh);
}
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken)refreshToken;
Date expiration = expiringRefreshToken.getExpiration();
if (expiration != null) {
int seconds =
Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L).intValue();
conn.expire(refreshToAccessKey, (long)seconds);
conn.expire(accessToRefreshKey, (long)seconds);
}
}
}
conn.closePipeline();
} finally {
conn.close();
}
}
private static String getApprovalKey(OAuth2Authentication authentication) {
String userName =
authentication.getUserAuthentication() == null ? "" : authentication.getUserAuthentication().getName();
return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
}
private static String getApprovalKey(String clientId, String userName) {
return clientId + (userName == null ? "" : ":" + userName);
}
@Override
public void removeAccessToken(OAuth2AccessToken accessToken) {
this.removeAccessToken(accessToken.getValue());
}
@Override
public OAuth2AccessToken readAccessToken(String tokenValue) {
byte[] key = this.serializeKey("access:" + tokenValue);
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(key);
} finally {
conn.close();
}
OAuth2AccessToken var5 = this.deserializeAccessToken(bytes);
return var5;
}
public void removeAccessToken(String tokenValue) {
byte[] accessKey = this.serializeKey("access:" + tokenValue);
byte[] authKey = this.serializeKey("auth:" + tokenValue);
byte[] accessToRefreshKey = this.serializeKey("access_to_refresh:" + tokenValue);
RedisConnection conn = this.getConnection();
try {
conn.openPipeline();
conn.get(accessKey);
conn.get(authKey);
conn.del(new byte[][] {accessKey});
conn.del(new byte[][] {accessToRefreshKey});
conn.del(new byte[][] {authKey});
List<Object> results = conn.closePipeline();
byte[] access = (byte[])((byte[])results.get(0));
byte[] auth = (byte[])((byte[])results.get(1));
OAuth2Authentication authentication = this.deserializeAuthentication(auth);
if (authentication != null) {
String key = this.authenticationKeyGenerator.extractKey(authentication);
byte[] authToAccessKey = this.serializeKey("auth_to_access:" + key);
byte[] unameKey = this.serializeKey("uname_to_access:" + getApprovalKey(authentication));
byte[] clientId =
this.serializeKey("client_id_to_access:" + authentication.getOAuth2Request().getClientId());
conn.openPipeline();
conn.del(new byte[][] {authToAccessKey});
conn.sRem(unameKey, new byte[][] {access});
conn.sRem(clientId, new byte[][] {access});
conn.del(new byte[][] {this.serialize("access:" + key)});
conn.closePipeline();
}
} finally {
conn.close();
}
}
@Override
public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
byte[] refreshKey = this.serializeKey("refresh:" + refreshToken.getValue());
byte[] refreshAuthKey = this.serializeKey("refresh_auth:" + refreshToken.getValue());
byte[] serializedRefreshToken = this.serialize((Object)refreshToken);
RedisConnection conn = this.getConnection();
try {
conn.openPipeline();
if (springDataRedis_2_0) {
try {
this.redisConnectionSet_2_0.invoke(conn, refreshKey, serializedRefreshToken);
this.redisConnectionSet_2_0.invoke(conn, refreshAuthKey, this.serialize((Object)authentication));
} catch (Exception var13) {
throw new RuntimeException(var13);
}
} else {
conn.set(refreshKey, serializedRefreshToken);
conn.set(refreshAuthKey, this.serialize((Object)authentication));
}
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken)refreshToken;
Date expiration = expiringRefreshToken.getExpiration();
if (expiration != null) {
int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L).intValue();
conn.expire(refreshKey, (long)seconds);
conn.expire(refreshAuthKey, (long)seconds);
}
}
conn.closePipeline();
} finally {
conn.close();
}
}
@Override
public OAuth2RefreshToken readRefreshToken(String tokenValue) {
byte[] key = this.serializeKey("refresh:" + tokenValue);
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(key);
} finally {
conn.close();
}
OAuth2RefreshToken var5 = this.deserializeRefreshToken(bytes);
return var5;
}
@Override
public void removeRefreshToken(OAuth2RefreshToken refreshToken) {
this.removeRefreshToken(refreshToken.getValue());
}
public void removeRefreshToken(String tokenValue) {
byte[] refreshKey = this.serializeKey("refresh:" + tokenValue);
byte[] refreshAuthKey = this.serializeKey("refresh_auth:" + tokenValue);
byte[] refresh2AccessKey = this.serializeKey("refresh_to_access:" + tokenValue);
byte[] access2RefreshKey = this.serializeKey("access_to_refresh:" + tokenValue);
RedisConnection conn = this.getConnection();
try {
conn.openPipeline();
conn.del(new byte[][] {refreshKey});
conn.del(new byte[][] {refreshAuthKey});
conn.del(new byte[][] {refresh2AccessKey});
conn.del(new byte[][] {access2RefreshKey});
conn.closePipeline();
} finally {
conn.close();
}
}
@Override
public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
this.removeAccessTokenUsingRefreshToken(refreshToken.getValue());
}
private void removeAccessTokenUsingRefreshToken(String refreshToken) {
byte[] key = this.serializeKey("refresh_to_access:" + refreshToken);
List<Object> results = null;
RedisConnection conn = this.getConnection();
try {
conn.openPipeline();
conn.get(key);
conn.del(new byte[][] {key});
results = conn.closePipeline();
} finally {
conn.close();
}
if (results != null) {
byte[] bytes = (byte[])((byte[])results.get(0));
String accessToken = this.deserializeString(bytes);
if (accessToken != null) {
this.removeAccessToken(accessToken);
}
}
}
private List<byte[]> getByteLists(byte[] approvalKey, RedisConnection conn) {
Long size = conn.sCard(approvalKey);
List<byte[]> byteList = new ArrayList(size.intValue());
Cursor cursor = conn.sScan(approvalKey, ScanOptions.NONE);
while (cursor.hasNext()) {
byteList.add((byte[])cursor.next());
}
return byteList;
}
@Override
public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
byte[] approvalKey = this.serializeKey("uname_to_access:" + getApprovalKey(clientId, userName));
List<byte[]> byteList = null;
RedisConnection conn = this.getConnection();
try {
byteList = this.getByteLists(approvalKey, conn);
} finally {
conn.close();
}
if (byteList != null && byteList.size() != 0) {
List<OAuth2AccessToken> accessTokens = new ArrayList(byteList.size());
Iterator var7 = byteList.iterator();
while (var7.hasNext()) {
byte[] bytes = (byte[])var7.next();
OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
accessTokens.add(accessToken);
}
return Collections.unmodifiableCollection(accessTokens);
} else {
return Collections.emptySet();
}
}
@Override
public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
byte[] key = this.serializeKey("client_id_to_access:" + clientId);
List<byte[]> byteList = null;
RedisConnection conn = this.getConnection();
try {
byteList = this.getByteLists(key, conn);
} finally {
conn.close();
}
if (byteList != null && byteList.size() != 0) {
List<OAuth2AccessToken> accessTokens = new ArrayList(byteList.size());
Iterator var6 = byteList.iterator();
while (var6.hasNext()) {
byte[] bytes = (byte[])var6.next();
OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
accessTokens.add(accessToken);
}
return Collections.unmodifiableCollection(accessTokens);
} else {
return Collections.emptySet();
}
}
}
CodeController,ImageCodeValidateFilter,KaptchaImageCodeConfig
验证码 可以参考上文
@Controller
public class CodeController {
Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private DefaultKaptcha defaultKaptcha;
@RequestMapping("/code/image")
public void imageCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 1. 获取验证码字符串
String code = defaultKaptcha.createText();
logger.info("生成的图形验证码是:" + code);
// 2. 字符串把它放到session中
request.getSession().setAttribute(CODE_SESSION_KEY, code);
// 3. 获取验证码图片
BufferedImage image = defaultKaptcha.createImage(code);
// 4. 将验证码图片把它写出去
ServletOutputStream out = response.getOutputStream();
ImageIO.write(image, "jpg", out);
}
}
@Component("imageCodeValidateFilter")
public class ImageCodeValidateFilter extends OncePerRequestFilter {
@Autowired
SecurityProperties securityProperties;
@Autowired
CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 1. 如果是post方式 的登录请求,则校验输入的验证码是否正确
if (LOGIN_ROUTE.equals(request.getRequestURI()) && request.getMethod().equalsIgnoreCase("post")) {
try {
// 校验验证码合法性
validate(request);
} catch (AuthenticationException e) {
// 交给失败处理器进行处理异常
customAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);
// 一定要记得结束
return;
}
}
// 放行请求
filterChain.doFilter(request, response);
}
private void validate(HttpServletRequest request) {
// 先获取seesion中的验证码
String sessionCode = (String)request.getSession().getAttribute(CODE_SESSION_KEY);
// 获取用户输入的验证码
String inpuCode = request.getParameter("code");
// 判断是否正确
if (StringUtils.isBlank(inpuCode)) {
throw new ValidateCodeException("验证码不能为空");
}
if (!inpuCode.equalsIgnoreCase(sessionCode)) {
throw new ValidateCodeException("验证码输入错误");
}
}
}
@Configuration
public class KaptchaImageCodeConfig {
@Bean
public DefaultKaptcha getDefaultKaptcha() {
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
properties.setProperty(Constants.KAPTCHA_BORDER, "yes");
properties.setProperty(Constants.KAPTCHA_BORDER_COLOR, "192,192,192");
properties.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "110");
properties.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "36");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "28");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES, "宋体");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
// 图片效果
properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}