zhibing.pu
2024-08-19 0e9d35f36f3b022a2dafa189198293c836e41dc0
增加网关账户校验
5个文件已修改
191 ■■■■ 已修改文件
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-gateway/pom.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java 139 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java
@@ -36,6 +36,11 @@
     * 用户标识
     */
    public static final String USER_KEY = "user_key";
    /**
     * 用户类型(system/applet)
     */
    public static final String USER_TYPE = "user_type";
    /**
     * 小程序登录用户标识
     */
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java
@@ -62,6 +62,31 @@
    {
        return getValue(claims, SecurityConstants.USER_KEY);
    }
    /**
     * 根据令牌获取用户类型
     *
     * @param token 令牌
     * @return 用户类型
     */
    public static String getUserType(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, SecurityConstants.USER_TYPE);
    }
    /**
     * 根据令牌获取用户类型
     *
     * @param claims 身份信息
     * @return 用户类型
     */
    public static String getUserType(Claims claims)
    {
        return getValue(claims, SecurityConstants.USER_TYPE);
    }
    /**
     * 小程序根据令牌获取用户标识
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java
@@ -60,6 +60,7 @@
        Map<String, Object> claimsMap = new HashMap<String, Object>();
        claimsMap.put(SecurityConstants.USER_KEY, token);
        claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
        claimsMap.put(SecurityConstants.USER_TYPE, "system");
        claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName);
        // 接口返回信息
@@ -82,6 +83,7 @@
        Map<String, Object> claimsMap = new HashMap<String, Object>();
        claimsMap.put(SecurityConstants.USER_APPLET_KEY, token);
        claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
        claimsMap.put(SecurityConstants.USER_TYPE, "applet");
        claimsMap.put(SecurityConstants.DETAILS_USERNAME, name);
        // 接口返回信息
        Map<String, Object> rspMap = new HashMap<String, Object>();
ruoyi-gateway/pom.xml
@@ -114,18 +114,14 @@
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-websocket</artifactId>-->
<!--            <optional>true</optional>-->
<!--        </dependency>-->
        <!--hutool-all-->
<!--        <dependency>-->
<!--            <groupId>cn.hutool</groupId>-->
<!--            <artifactId>hutool-all</artifactId>-->
<!--            <version>5.0.3</version>-->
<!--        </dependency>-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-api-account</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-api-system</artifactId>
        </dependency>
    </dependencies>
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java
@@ -1,18 +1,17 @@
package com.ruoyi.gateway.filter;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;
import com.ruoyi.account.api.feignClient.AppUserClient;
import com.ruoyi.account.api.model.TAppUser;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.feignClient.SysUserClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.ruoyi.common.core.constant.CacheConstants;
@@ -25,14 +24,12 @@
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.gateway.config.properties.IgnoreWhiteProperties;
import io.jsonwebtoken.Claims;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
 * 网关鉴权
@@ -50,6 +47,13 @@
    @Autowired
    private RedisService redisService;
    
    @Resource
    private AppUserClient appUserClient;
    @Resource
    private SysUserClient sysUserClient;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
@@ -61,24 +65,26 @@
        if (StringUtils.matches(url, ignoreWhite.getWhites())) {
            return chain.filter(exchange);
        }
        //防抖校验
        try {
            antiShake(request);
        }catch (Exception e){
            log.error("[重复提交]请求路径:{}", exchange.getRequest().getPath());
            return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage(), HttpStatus.SUCCESS);
        }
        //校验账户是否有效
        try {
            verifyToken(request);
            verifyAccount(request);
        }catch (Exception e){
            return unauthorizedResponse(exchange, e.getMessage());
        }
        String token = getToken(request);
        if (StringUtils.isEmpty(token)) {
            return unauthorizedResponse(exchange, "令牌不能为空");
        }
        Claims claims = JwtUtils.parseToken(token);
        if (claims == null) {
            return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");
        }
        String userkey = JwtUtils.getUserKey(claims);
        boolean islogin = redisService.hasKey(getTokenKey(userkey));
        if (!islogin) {
            return unauthorizedResponse(exchange, "登录状态已过期");
        }
        String userid = JwtUtils.getUserId(claims);
        String username = JwtUtils.getUserName(claims);
        if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) {
            return unauthorizedResponse(exchange, "令牌验证失败");
        }
        
        // 设置用户信息到请求
        addHeader(mutate, SecurityConstants.USER_KEY, userkey);
@@ -130,9 +136,94 @@
    /**
     * 防抖处理
     */
    public void antiShake(ServerHttpRequest request) throws Exception{
        HttpHeaders headers = request.getHeaders();
        String client = headers.getFirst("client");
        String timestamp = headers.getFirst("timestamp");
        String url = request.getURI().getPath();
        Map<String, Object> cacheMap = redisService.getCacheMap(client);
        if(null == cacheMap){
            cacheMap = new HashMap<>();
            cacheMap.put(url, timestamp);
            redisService.setCacheMap(client, cacheMap);
        }else{
            Object o = cacheMap.get(url);
            if(null == o){
                cacheMap.put(url, timestamp);
            }else{
                Long old_timestamp = Long.valueOf(o.toString());
                Long new_timestamp = Long.valueOf(timestamp);
                //两个请求时间差小于1秒,判定为重复提交
                if((new_timestamp - old_timestamp) <= 1000){
                    throw new RuntimeException("重复提交");
                }else{
                    cacheMap.put(url, timestamp);
                }
            }
            redisService.setCacheMap(client, cacheMap);
        }
    }
    
    
    
    /**
     * 验证token
     * @param request
     * @throws Exception
     */
    public void verifyToken(ServerHttpRequest request) throws Exception{
        String token = getToken(request);
        if (StringUtils.isEmpty(token)) {
            throw new RuntimeException("令牌不能为空");
        }
        Claims claims = JwtUtils.parseToken(token);
        if (claims == null) {
            throw new RuntimeException("令牌已过期或验证不正确!");
        }
        String userkey = JwtUtils.getUserKey(claims);
        boolean islogin = redisService.hasKey(getTokenKey(userkey));
        if (!islogin) {
            throw new RuntimeException("登录状态已过期");
        }
        String userid = JwtUtils.getUserId(claims);
        String username = JwtUtils.getUserName(claims);
        if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) {
            throw new RuntimeException("令牌验证失败");
        }
    }
    /**
     * 校验账户是否有效
     * @param request
     * @throws Exception
     */
    public void verifyAccount(ServerHttpRequest request) throws Exception{
        String token = getToken(request);
        Claims claims = JwtUtils.parseToken(token);
        String userid = JwtUtils.getUserId(claims);
        String userType = JwtUtils.getUserType(claims);
        //管理后台用户
        if ("system".equals(userType)) {
            SysUser sysUser = sysUserClient.getSysUser(Long.valueOf(userid)).getData();
            if(null == sysUser || "2".equals(sysUser.getDelFlag())){
                throw new RuntimeException("无效的账户");
            }
            if("1".equals(sysUser.getStatus())){
                throw new RuntimeException("账户已被停用,请联系系统管理员!");
            }
        }
        //小程序用户
        if ("applet".equals(userType)) {
            TAppUser appUser = appUserClient.getUserById(Long.valueOf(userid)).getData();
            if(null == appUser || appUser.getDelFlag() || 3 == appUser.getStatus()){
                throw new RuntimeException("无效的账户");
            }
            if(2 == appUser.getStatus()){
                throw new RuntimeException("账户已被冻结,请联系系统管理员!");
            }
        }
    }
    
    @Override
    public int getOrder() {