系统白名单接口添加自定义验证(模仿oauth2.0),防安全扫描不通过

1.获取临时code

bash 复制代码
/**
     * 获取临时code
     */
    @GetMapping("/getCode")
    public ActionResult<String> getCode() {

        String randomCode = RandomUtil.randomNumbers(6);
        // 300秒
        redisUtil.insert(VALIDCODE + randomCode, randomCode, 300);
       return ActionResult.success("success",randomCode);

    }

2.获取token

2.1 请求参数

bash 复制代码
import lombok.Data;

@Data
public class StaticTokenDTO {

    // 临时code
    private Integer code;

    // 自定义的appID
    private String appId;

    // 自定义的秘钥
    private String appSecret;

}

2.2配置的应用凭证

bash 复制代码
// 配置的应用凭证 实际项目中应该从数据库或配置文件中获取
    private static final Map<String, String> APP_CREDENTIALS = new HashMap<>();
    static {
        APP_CREDENTIALS.put("remote001","MIGfMA0GCSqGSIb3");
    }

2.3 获取静态token

bash 复制代码
private static final String VALIDCODE = "validcode:";
    private static final String ACCESS_TOKEN_PREFIX = "access_token:";
    private static final long ACCESS_TOKEN_EXPIRE = 7200; // 2小时过期
    private static final String REFRESH_TOKEN_PREFIX = "refresh_token:";
    private static final long REFRESH_TOKEN_EXPIRE = 604800; // 7天过期


/**
     * 获取静态token
     */
    @PostMapping("/getStaticToken")
    public ActionResult<Map<String, Object>> getStaticToken(StaticTokenDTO staticToken) {
        try {
            // 1. 参数验证
            if (staticToken == null) {
                return ActionResult.fail("参数不能为空");
            }

            if (ObjectUtil.isEmpty(staticToken.getCode()) || ObjectUtil.isEmpty(redisUtil.getString(VALIDCODE + staticToken.getCode()))) {
                return ActionResult.fail("code 验证失败");
            }

            if(StringUtils.isBlank(staticToken.getAppId()) || StringUtils.isBlank(staticToken.getAppSecret())){
                return ActionResult.fail("appId 和 appSecret 不能为空");
            }
            
            // 2. 验证应用凭证
            String expectedSecret = APP_CREDENTIALS.get(staticToken.getAppId());
            if (expectedSecret == null) {
                return ActionResult.fail("无效的 appId");
            }
            
            if (!expectedSecret.equals(staticToken.getAppSecret())) {
                return ActionResult.fail("appSecret 验证失败");
            }

            // 删除 验证码
            redisUtil.remove(VALIDCODE + staticToken.getCode());
            
            // 3. 生成安全的 accessToken
            String accessToken = generateSecureAccessToken(staticToken);
            
            // 4. 将 token 存储到 Redis 中,设置过期时间
            String tokenKey = ACCESS_TOKEN_PREFIX + accessToken;
            Map<String, Object> tokenInfo = new HashMap<>();
            tokenInfo.put("appId", staticToken.getAppId());
            tokenInfo.put("createTime", System.currentTimeMillis());
            tokenInfo.put("expireTime", System.currentTimeMillis() + ACCESS_TOKEN_EXPIRE * 1000);
            
            redisUtil.insert(tokenKey, JsonUtil.getObjectToString(tokenInfo), ACCESS_TOKEN_EXPIRE);
            
            // 4.1 生成并存储 refreshToken(与 accessToken 绑定)
            String refreshToken = RandomUtil.randomString(32);
            String refreshTokenKey = REFRESH_TOKEN_PREFIX + refreshToken;
            long refreshExpire = REFRESH_TOKEN_EXPIRE;
            Map<String, Object> refreshInfo = new HashMap<>();
            refreshInfo.put("appId", staticToken.getAppId());
            refreshInfo.put("bindAccessToken", accessToken);
            refreshInfo.put("createTime", System.currentTimeMillis());
            refreshInfo.put("expireTime", System.currentTimeMillis() + refreshExpire * 1000);
            redisUtil.insert(refreshTokenKey, JsonUtil.getObjectToString(refreshInfo), refreshExpire);
            
            // 5. 返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("accessToken", accessToken);
            result.put("refreshToken", refreshToken);
            result.put("expiresIn", ACCESS_TOKEN_EXPIRE);
            result.put("tokenType", "Bearer");
            
            return ActionResult.success(result);
            
        } catch (Exception e) {
            return ActionResult.fail("生成 token 失败: " + e.getMessage());
        }
    }

3.刷新 token 调用刷新token方法后,以前的accessToken 被失效

bash 复制代码
@PostMapping("/refreshToken")
    public ActionResult<Map<String, Object>> refreshToken(String refreshToken) {
        try {
            // 校验 refreshToken 是否有效
            if (StringUtils.isBlank(refreshToken)) {
                return ActionResult.fail("无效的 refreshToken");
            }

            String refreshKey = REFRESH_TOKEN_PREFIX + refreshToken;
            String refreshInfoStr = (String) redisUtil.getString(refreshKey);
            if (StringUtils.isBlank(refreshInfoStr)) {
                return ActionResult.fail("无效的 refreshToken");
            }

            Map<String, Object> refreshInfo = JsonUtil.stringToMap(refreshInfoStr);
            String appId = String.valueOf(refreshInfo.get("appId"));
            String bindAccessToken = String.valueOf(refreshInfo.get("bindAccessToken"));

            // 尝试解析旧 accessToken 获取 code
            Integer code = null;
            try {
                String jwtSecret = getJwtSecret();
                Map<String, Object> claims = Jwts.parser()
                        .setSigningKey(jwtSecret)
                        .parseClaimsJws(bindAccessToken)
                        .getBody();
                Object codeObj = claims.get("code");
                if (codeObj instanceof Integer) {
                    code = (Integer) codeObj;
                } else if (codeObj instanceof Number) {
                    code = ((Number) codeObj).intValue();
                }
            } catch (Exception ignore) {
            }

            // 失效旧的 accessToken 和 refreshToken
            if (StringUtils.isNotBlank(bindAccessToken)) {
                redisUtil.remove(ACCESS_TOKEN_PREFIX + bindAccessToken);
            }
            redisUtil.remove(refreshKey);

            // 生成新的 accessToken
            StaticTokenDTO staticTokenDTO = new StaticTokenDTO();
            staticTokenDTO.setAppId(appId);
            staticTokenDTO.setAppSecret(APP_CREDENTIALS.get(appId));
            staticTokenDTO.setCode(code);
            String newAccessToken = generateSecureAccessToken(staticTokenDTO);

            // 存储新的 accessToken
            String newAccessTokenKey = ACCESS_TOKEN_PREFIX + newAccessToken;
            Map<String, Object> tokenInfo = new HashMap<>();
            tokenInfo.put("appId", appId);
            tokenInfo.put("createTime", System.currentTimeMillis());
            tokenInfo.put("expireTime", System.currentTimeMillis() + ACCESS_TOKEN_EXPIRE * 1000);
            redisUtil.insert(newAccessTokenKey, JsonUtil.getObjectToString(tokenInfo), ACCESS_TOKEN_EXPIRE);

            // 生成并存储新的 refreshToken(与新的 accessToken 绑定)
            String newRefreshToken = RandomUtil.randomString(32);
            String newRefreshTokenKey = REFRESH_TOKEN_PREFIX + newRefreshToken;
            long newRefreshExpire = REFRESH_TOKEN_EXPIRE;
            Map<String, Object> newRefreshInfo = new HashMap<>();
            newRefreshInfo.put("appId", appId);
            newRefreshInfo.put("bindAccessToken", newAccessToken);
            newRefreshInfo.put("createTime", System.currentTimeMillis());
            newRefreshInfo.put("expireTime", System.currentTimeMillis() + newRefreshExpire * 1000);
            redisUtil.insert(newRefreshTokenKey, JsonUtil.getObjectToString(newRefreshInfo), newRefreshExpire);

            Map<String, Object> result = new HashMap<>();
            result.put("accessToken", newAccessToken);
            result.put("refreshToken", newRefreshToken);
            result.put("expiresIn", ACCESS_TOKEN_EXPIRE);
            result.put("tokenType", "Bearer");

            return ActionResult.success(result);
        } catch (Exception e) {
            return ActionResult.fail("刷新 token 失败: " + e.getMessage());
        }
    }

4.验证token

bash 复制代码
/**
     * 验证访问令牌
     */
    private boolean validateAccessToken(String accessToken) {
        try {
            if (StringUtils.isBlank(accessToken)) {
                return true;
            }

            // 1. 检查 Redis 中是否存在该 token
            String tokenKey = ACCESS_TOKEN_PREFIX + accessToken;
            String tokenInfoStr = (String) redisUtil.getString(tokenKey);
            if (StringUtils.isBlank(tokenInfoStr)) {
                return true;
            }

            // 2. 验证 JWT 签名和过期时间
            String jwtSecret = getJwtSecret();
            try {
                Jwts.parser()
                        .setSigningKey(jwtSecret)
                        .parseClaimsJws(accessToken);
                return false;
            } catch (Exception e) {
                // JWT 验证失败,删除 Redis 中的记录
                redisUtil.remove(tokenKey);
                return true;
            }

        } catch (Exception e) {
            return true;
        }
    }

5.生成安全的访问令牌方法

bash 复制代码
/**
     * 生成安全的访问令牌
     */
    private String generateSecureAccessToken(StaticTokenDTO staticToken) {
        long currentTime = System.currentTimeMillis();
        long expireTime = currentTime + ACCESS_TOKEN_EXPIRE * 1000;

        // 生成随机数防止重放攻击
        String nonce = RandomUtil.randomString(16);

        // 创建 JWT payload
        Map<String, Object> claims = new HashMap<>();
        claims.put("appId", staticToken.getAppId());
        claims.put("nonce", nonce);
        claims.put("timestamp", currentTime);
        claims.put("iat", currentTime / 1000);
        claims.put("exp", expireTime / 1000);
        claims.put("code", staticToken.getCode());

        // 使用 HMAC-SHA256 签名生成 JWT
        String jwtSecret = getJwtSecret();
        String jwt = Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(new Date(currentTime))
                .setExpiration(new Date(expireTime))
                .signWith(SignatureAlgorithm.HS256, jwtSecret)
                .compact();

        return jwt;
    }

6.获取 JWT 密钥 (这个可以随便写)

bash 复制代码
/**
     * 获取 JWT 密钥
     */
    private String getJwtSecret() {

        return AuthConsts.JWT_SECRET;
    }

7.测试

bash 复制代码
@GetMapping("/getEnterpriseGradePage")
    public ActionResult<IPage<EnterpriseGradeVO>> getEnterpriseGradePage(HttpServletRequest request, Pagination page) {
        String authorization = request.getHeader("Authorization");
        if (validateAccessToken(authorization)) {
            return ActionResult.fail("无效的 accessToken");
        }

        return ActionResult.success(remoteService.getEnterpriseGradePage(page));
    }

8.主要依赖

bash 复制代码
<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
相关推荐
努力努力再努力wz3 小时前
【C++进阶系列】:万字详解特殊类以及设计模式
java·linux·运维·开发语言·数据结构·c++·设计模式
青云交3 小时前
Java 大视界 -- Java 大数据在智慧交通自动驾驶仿真与测试数据处理中的应用
java·大数据·自动驾驶·数据存储·算法优化·智慧交通·测试数据处理
reasonsummer3 小时前
【办公类-115-05】20250920职称资料上传04——PDF和PDF合并PDF、图片和PDF合并PDF(十三五PDF+十四五图片)
java·python·pdf
Mcband3 小时前
Apache Commons IO:文件流处理利器,让Java IO操作更简单
java·开发语言·apache
缺点内向3 小时前
Java:将 Word 文档转换为密码保护的 PDF 文件
java·pdf·word
独行soc3 小时前
2025年渗透测试面试题总结-105(题目+回答)
网络·python·安全·web安全·adb·渗透测试·安全狮
骑士雄师4 小时前
Java 泛型中级面试题及答案
java·开发语言·面试
恒创科技HK5 小时前
高防服务器分为哪几种?香港高防服务器有什么特点?
运维·服务器·安全
.格子衫.9 小时前
Spring Boot 原理篇
java·spring boot·后端