zf
2024-03-25 cd02923a7ce1ffa004b3abbb7af67ab6173dd99d
safePlatfrom-out-web/src/main/java/com/gkhy/safePlatform/config/security/TokenAuthenticationFilter.java
@@ -1,155 +1,161 @@
//package com.gkhy.safePlatform.config.security;
//
//import com.alibaba.fastjson.JSONArray;
//import com.alibaba.fastjson.JSONObject;
//import com.gkhy.safePlatform.account.model.cache.CacheUser;
//import com.gkhy.safePlatform.account.rpc.apimodel.NameService;
//import com.gkhy.safePlatform.commons.config.token.TokenConfig;
//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.RedisUtils;
//import com.gkhy.safePlatform.commons.utils.StringUtils;
//import com.gkhy.safePlatform.commons.vo.ResultVO;
//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.List;
//
///**
//* @Description: token登录过滤器
//*/
//@Component
//public class TokenAuthenticationFilter extends OncePerRequestFilter  {
//
//    @Autowired
//    private TokenConfig tokenConfig;
//    @Autowired
//    private RedisUtils redisUtil;
//    @DubboReference(check = false)
//    private NameService nameService;
//
//
//
//    @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.getError()));
//        } catch (Exception e) {
//            e.printStackTrace();
//            this.writeJSON(req, resp, new ResultVO<>(ResultCodes.SERVER_ERROR));
//        }
//
//
//    }
//
//
//    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req,HttpServletResponse resp) {
//        // header获取token
//        String authToken = req.getHeader(tokenConfig.getHeader());
//        String loginUserId = req.getHeader(tokenConfig.getLoginUserHeader());
//
//        if(authToken != null) {
//            // header 传入 userId
//            if (StringUtils.isBlank(loginUserId)) {
//                throw new BusinessException(ResultCodes.CLIENT_CREDENTIALS_LACK);
//            }
//            // 登录成功时,会将权限数据存入redis
//            // 这里是验证获取权限信息
//            // 1.从redis中获取对应该用户的权限信息
//            String accessTokenKey = RedisKeyEnum.authKey(RedisKeyEnum.AUTH_TOKEN, loginUserId);
//            Object o = redisUtil.get(accessTokenKey);
//            // 2.token是否存在
//            if (o == null) {
//                // 是否存在
//                throw new BusinessException(ResultCodes.CLIENT_CREDENTIALS_SIGN_INVALID);
//            }else{
//                Long userId = Long.valueOf(loginUserId);
//                CacheUser cacheUser = JSONObject.parseObject(o.toString(), CacheUser.class);
//                assert userId.equals(cacheUser.getUserId());
//                if ( !authToken.equals(cacheUser.getAccessToken())) {
//                    throw new BusinessException(ResultCodes.CLIENT_CREDENTIALS_TOKEN_INVALID);
//                }
//
//                // 3.redis获取权限
//                String authoritiesKey = RedisKeyEnum.authKey(RedisKeyEnum.AUTH_AUTHORITIES, userId);
//                Object oo = redisUtil.get(authoritiesKey);
//                List<GrantedAuthority> authorities;
//                // 4.redis中是否存在
//                if (oo != null) {
//                    // 5.存在
//                    String json = oo.toString();
//                    authorities = JSONArray.parseArray(json, GrantedAuthority.class);
//                }else {
//                    authorities = new ArrayList<>();
//                    // 6.不存在=>数据库查询
//                    List<String> roleCodes = nameService.getUserRoleCodeByUserId(userId);
//                    // role
//                    for (String roleCode : roleCodes) {
//                        SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_" + roleCode);
//                        authorities.add(simpleGrantedAuthority);
//                    }
//
//                    // permission
//                    List<String> permissions = nameService.getUserPermissionByUserId(userId);
//                    for (String permission : permissions) {
//                        SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(permission);
//                        authorities.add(simpleGrantedAuthority);
//                    }
//                }
//
//                // security对象中存入登陆者信息
//                return new UsernamePasswordAuthenticationToken(userId,authToken,authorities);
//
//            }
//
//
//
//
//
//
//        }
//        return null;
//    }
//
//
//
//    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();
//    }
//}
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<ContextCacheUser> 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<ContextCacheUser> 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();
    }
}