1、授权过滤,只要实现AuthConfigAdapter接口
2、利用Redis token超时时间,用户访问后台续时
效果
@Component
public class AuthFilter implements Filter {
private static Logger logger = LoggerFactory.getLogger(AuthFilter.class);
@Autowired
private AuthConfigAdapter authConfigAdapter;
@Autowired(required = false)
private HttpHandler httpHandler;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
List<String> excludePathPatterns = authConfigAdapter.excludePathPatterns();
// 如果匹配不需要授权的路径,就不需要校验是否需要授权
if (CollectionUtil.isNotEmpty(excludePathPatterns)) {
for (String excludePathPattern : excludePathPatterns) {
AntPathMatcher pathMatcher = new AntPathMatcher();
if (pathMatcher.match(excludePathPattern, req.getRequestURI())) {
chain.doFilter(req, resp);
return;
}
}
}
String accessToken = req.getHeader("Authorization");
String sysType = req.getHeader("SysType");
if (StrUtil.isBlank(accessToken)) {
logger.error("{} : {}", ResponseEnum.UNAUTHORIZED, req.getRequestURI());
httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
return;
}
if(StrUtil.isBlank(sysType)){
logger.error("{} : {}", ResponseEnum.UNAUTHORIZED, req.getRequestURI());
httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
return;
}
// 校验token,并返回用户信息
ServerResponseEntity<UserInfoInTokenBO> userInfoInTokenVoServerResponseEntity = TokenSecurity.checkToken(accessToken);
if (!userInfoInTokenVoServerResponseEntity.isSuccess()) {
logger.error("{} : {}", ServerResponseEntity.transform(userInfoInTokenVoServerResponseEntity), req.getRequestURI());
httpHandler.printServerResponseToWeb(ServerResponseEntity.transform(userInfoInTokenVoServerResponseEntity));
return;
}
if(userInfoInTokenVoServerResponseEntity.getData().getSysType() != Integer.parseInt(sysType)){
httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
return;
}
AuthUserContext.set(BeanUtil.copyProperties(userInfoInTokenVoServerResponseEntity.getData(), UserInfoInTokenBO.class));
try {
chain.doFilter(req, resp);
} finally {
AuthUserContext.clean();
}
}
}
@Component
@RefreshScope
public class TokenSecurity {
private static final Logger logger = LoggerFactory.getLogger(TokenSecurity.class);
private static RedisTemplate<Object, Object> redisTemplate = null;
private static StringRedisTemplate stringRedisTemplate;
public TokenSecurity(RedisTemplate<Object, Object> redisTemplate, StringRedisTemplate stringRedisTemplate) {
TokenSecurity.redisTemplate = redisTemplate;
TokenSecurity.stringRedisTemplate = stringRedisTemplate;
}
public static ServerResponseEntity<UserInfoInTokenBO> checkToken(String accessToken) {
ServerResponseEntity<UserInfoInTokenBO> userInfoByAccessTokenResponse = getUserInfoByAccessToken(accessToken, true);
if (!userInfoByAccessTokenResponse.isSuccess()) {
return ServerResponseEntity.transform(userInfoByAccessTokenResponse);
}
return ServerResponseEntity.success(userInfoByAccessTokenResponse.getData());
}
public static ServerResponseEntity<UserInfoInTokenBO> getUserInfoByAccessToken(String accessToken, boolean needDecrypt) {
if (StrUtil.isBlank(accessToken)) {
return ServerResponseEntity.showFailMsg("accessToken is blank");
}
String realAccessToken;
if (needDecrypt) {
ServerResponseEntity<String> decryptTokenEntity = decryptToken(accessToken);
if (!decryptTokenEntity.isSuccess()) {
return ServerResponseEntity.transform(decryptTokenEntity);
}
realAccessToken = decryptTokenEntity.getData();
}
else {
realAccessToken = accessToken;
}
UserInfoInTokenBO userInfoInTokenBO = (UserInfoInTokenBO) redisTemplate.opsForValue().get(getAccessKey(realAccessToken));
if (userInfoInTokenBO == null) {
return ServerResponseEntity.fail(ResponseEnum.CLEAN_TOKEN);
}
String refreshToken = stringRedisTemplate.opsForValue().get(CacheNames.REFRESH_TO_ACCESS + userInfoInTokenBO.getMobile());
if (StrUtil.isBlank(refreshToken)) {
return ServerResponseEntity.fail(ResponseEnum.CLEAN_TOKEN);
}
// 存在则token续时7200秒
redisTemplate.opsForValue().getAndExpire(getAccessKey(realAccessToken), 7200L, TimeUnit.SECONDS);
return ServerResponseEntity.success(userInfoInTokenBO);
}
public static String getAccessKey(String accessToken) {
return CacheNames.ACCESS + accessToken;
}
private static ServerResponseEntity<String> decryptToken(String data) {
String decryptStr;
String decryptToken;
try {
decryptStr = Base64.decodeStr(data);
decryptToken = decryptStr.substring(0,32);
// 创建token的时间,token使用时效性,防止攻击者通过一堆的尝试找到aes的密码,虽然aes是目前几乎最好的加密算法
long createTokenTime = Long.parseLong(decryptStr.substring(32,45));
// token的过期时间
int expiresIn = getExpiresIn();
long second = 1000L;
if (System.currentTimeMillis() - createTokenTime > expiresIn * second) {
return ServerResponseEntity.fail(ResponseEnum.TOKEN_ERROR);
}
}
catch (Exception e) {
logger.error(e.getMessage());
return ServerResponseEntity.fail(ResponseEnum.TOKEN_ERROR);
}
// 防止解密后的token是脚本,从而对redis进行攻击,uuid只能是数字和小写字母
if (!PrincipalUtil.isSimpleChar(decryptToken)) {
return ServerResponseEntity.fail(ResponseEnum.TOKEN_ERROR);
}
return ServerResponseEntity.success(decryptToken);
}
private static int getExpiresIn() {
// 3600秒
int expiresIn = 3600;
expiresIn = expiresIn * 24;
return expiresIn;
}
}