系统白名单接口添加自定义验证(模仿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>
相关推荐
闲人编程7 分钟前
Python与区块链:如何用Web3.py与以太坊交互
python·安全·区块链·web3.py·以太坊·codecapsule
lkbhua莱克瓦2414 分钟前
Java练习-正则表达式 1
java·笔记·正则表达式·github
yue00815 分钟前
C#类继承
java·开发语言·c#
凯芸呢1 小时前
Java中的数组(续)
java·开发语言·数据结构·算法·青少年编程·排序算法·idea
竹竹零1 小时前
JacksonUtil--序列化与反序列化
java·开发语言·windows
钱多多_qdd1 小时前
基础篇:IoC(三):Bean实例化策略InstantiationStrategy
java·spring
float_com1 小时前
【java基础语法】---- 综合训练
java
Dyan_csdn1 小时前
springboot系统设计选题3
java·spring boot·后端
碎碎思1 小时前
FPGA新闻速览-从漏洞到突破:FPGA技术在安全、架构与量子领域
安全·fpga开发
sheji34161 小时前
【开题答辩全过程】以 基于Java的旅游网站的设计与开发为例,包含答辩的问题和答案
java·开发语言·旅游