package com.gkhy.safePlatform.config.security; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.gkhy.safePlatform.account.rpc.apimodel.AccountAuthService; import com.gkhy.safePlatform.account.rpc.apimodel.AccountAuthService; import com.gkhy.safePlatform.commons.co.ContextCacheAuthority; import com.gkhy.safePlatform.commons.co.ContextCacheUser; import com.gkhy.safePlatform.commons.enums.RedisKeyEnum; import com.gkhy.safePlatform.commons.enums.ResultCodes; import com.gkhy.safePlatform.commons.exception.BusinessException; import com.gkhy.safePlatform.commons.utils.RPCUtils; import com.gkhy.safePlatform.commons.utils.RequestContextHolder; import com.gkhy.safePlatform.commons.utils.StringUtils; import com.gkhy.safePlatform.commons.vo.ResultVO; import com.gkhy.safePlatform.config.redis.RedisUtils; import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @Description: token登录过滤器 */ @Component public class TokenAuthenticationFilter extends OncePerRequestFilter { @Autowired private TokenConfig tokenConfig; @DubboReference(check = false) private AccountAuthService userAccountService; @Autowired private RedisUtils redisUtils; @Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException { try { //获取当前认证成功用户权限信息 UsernamePasswordAuthenticationToken authRequest = getAuthentication(req, resp); if (authRequest != null) { SecurityContextHolder.getContext().setAuthentication(authRequest); } // 执行下一个 filter 过滤器链 chain.doFilter(req, resp); } catch (BusinessException e) { // 返回异常 this.writeJSON(req, resp, new ResultVO<>(e.getCode(),e.getMessage())); } } private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req,HttpServletResponse resp) { // header获取token String authToken = req.getHeader(tokenConfig.getHeader()); if(authToken != null) { // 登录成功时,会将权限数据存入redis // 这里是验证获取权限信息 // 1.从redis中获取对应该用户的权限信息 String accessTokenKey = RedisKeyEnum.authKey(RedisKeyEnum.AUTH_TOKEN, authToken); Object o = redisUtils.get(accessTokenKey); // 2.token是否存在 if (o == null) { // 是否存在 uid未登录 throw new BusinessException(ResultCodes.CLIENT_CREDENTIALS_TOKEN_INVALID); }else{ // todo 可以不转换,建议rpc传入string String uid = o.toString(); Long userId = Long.valueOf(uid); String accessUserKey = RedisKeyEnum.authKey(RedisKeyEnum.AUTH_USER, userId); // 这里不做用户信息的token判断 放入登录 Long expireSecondsLeft = redisUtils.getExpireTime(accessTokenKey); // 60m 内请求则续期 时长为原本的有效时间 if (expireSecondsLeft != null && 0L < expireSecondsLeft && expireSecondsLeft < 60 * 60) { // 重置token:uid redisUtils.resetKeyExpireTime(accessTokenKey, tokenConfig.getExpiration()); // 重置uid:userInfo redisUtils.resetKeyExpireTime(accessUserKey, tokenConfig.getExpiration()); } // 获取用户信息 Object oo = redisUtils.get(accessUserKey); // 初始化 ContextCacheUser contextCacheUser = null; if (oo == null) { // 业务逻辑上是不会空的 // 实际操作可能会手动清空 ResultVO rpcResultVo = userAccountService.getCacheUserDetailByUid(userId); // 调用rpc返回的数据 没有token 所以得至少续上这次token contextCacheUser = this.getRpcResult(rpcResultVo); // 因为手动清空等原因,可能会丢失其他token数据,就不去一一搜索这个uid的token了 contextCacheUser.setAccessToken(Collections.singletonList(authToken)); }else{ // 正常的实际场景必定会走这里 // 推荐用jackson contextCacheUser = JSONObject.parseObject(oo.toString(), ContextCacheUser.class); } // threadLocal存入用户信息 RequestContextHolder.contextUserLocal.set(contextCacheUser); // security对象中存入登陆者信息 return new UsernamePasswordAuthenticationToken(contextCacheUser, authToken, contextCacheUser.getAuthorities()); } } return null; } /** * 获取rpc 返回的用户数据 * * @param rpcResultVo rpc返回数据 * @return 用户准备缓存的数据 */ private ContextCacheUser getRpcResult(ResultVO rpcResultVo) { if (!rpcResultVo.getCode().equals(ResultCodes.OK.getCode())) { throw new BusinessException(rpcResultVo.getCode(), rpcResultVo.getMsg()); } if (rpcResultVo.getData() == null) { throw new BusinessException(ResultCodes.RPC_DATA_NULL); } if (rpcResultVo.getData() instanceof ContextCacheUser) { return (ContextCacheUser) rpcResultVo.getData(); } else { throw new BusinessException(ResultCodes.RPC_DATA_TYPE_NOT_MATCH); } } protected void writeJSON(HttpServletRequest req, HttpServletResponse resp, ResultVO resultVO) throws IOException { // 设置编码格式 resp.setContentType("text/json;charset=utf-8"); // 处理跨域问题 resp.setHeader("Access-Control-Allow-Origin", "*"); resp.setHeader("Access-Control-Allow-Methods", "POST, GET"); //输出JSON PrintWriter out = resp.getWriter(); out.write(JSONObject.toJSONString(resultVO)); out.flush(); out.close(); } }