当前位置: 首页 > news >正文

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

1.获取临时code

/*** 获取临时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 请求参数

import lombok.Data;@Data
public class StaticTokenDTO {// 临时codeprivate Integer code;// 自定义的appIDprivate String appId;// 自定义的秘钥private String appSecret;}

2.2配置的应用凭证

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

2.3 获取静态token

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. 生成安全的 accessTokenString 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 被失效

@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 获取 codeInteger 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 和 refreshTokenif (StringUtils.isNotBlank(bindAccessToken)) {redisUtil.remove(ACCESS_TOKEN_PREFIX + bindAccessToken);}redisUtil.remove(refreshKey);// 生成新的 accessTokenStaticTokenDTO staticTokenDTO = new StaticTokenDTO();staticTokenDTO.setAppId(appId);staticTokenDTO.setAppSecret(APP_CREDENTIALS.get(appId));staticTokenDTO.setCode(code);String newAccessToken = generateSecureAccessToken(staticTokenDTO);// 存储新的 accessTokenString 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

/*** 验证访问令牌*/private boolean validateAccessToken(String accessToken) {try {if (StringUtils.isBlank(accessToken)) {return true;}// 1. 检查 Redis 中是否存在该 tokenString 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.生成安全的访问令牌方法

/*** 生成安全的访问令牌*/private String generateSecureAccessToken(StaticTokenDTO staticToken) {long currentTime = System.currentTimeMillis();long expireTime = currentTime + ACCESS_TOKEN_EXPIRE * 1000;// 生成随机数防止重放攻击String nonce = RandomUtil.randomString(16);// 创建 JWT payloadMap<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 签名生成 JWTString 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 密钥 (这个可以随便写)

/*** 获取 JWT 密钥*/private String getJwtSecret() {return AuthConsts.JWT_SECRET;}

7.测试

@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.主要依赖

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.7.0</version></dependency>
http://www.dtcms.com/a/465358.html

相关文章:

  • 校园服装网站建设预算手机软件应用市场
  • 【AI论文】ExGRPO:从经验中学习进行推理
  • 连接两个世界:QIR——量子-经典混合计算的编译器桥梁
  • 怎样制作网页链接教程狼雨seo教程
  • 第1章:初识Linux系统——连接网络NAT模式
  • CSS3 动画:从入门到精通
  • 在JavaScript / HTML中,所有转义字符(字符实体)
  • shopnc本地生活o2o网站源码网站开发软件怎么做
  • Vue3+socket.io 项目本地vite配置
  • wangEditor
  • Unity网络开发--超文本传输协议Http(1)
  • 从“用框架”到“控系统”——数据流、事件流、接口边界是如何形成的;
  • 有没有什么网站做泰国的东西做网站排名软件
  • 达梦数据库逻辑备份与还原
  • 如何解决Redis和数据库的一致性问题?
  • 新版saas餐饮外卖小程序源码/微信/支付宝/抖音/扫码点餐/DIY装修/美团代付/全开源
  • react 修复403页面无法在首页跳转问题
  • 学子网站建设郑州门户网站建设哪家好
  • 新乡网站优化公司看市场行情用什么软件
  • SQL注入原理与方法
  • 如何将Vue 项目转换为 Android App(使用Capacitor)
  • 输出端口原理图分析
  • 响应式网站建设有利于seo常熟做网站公司
  • php购物网站开发设计免费网站软件app
  • CTF攻防世界WEB精选基础入门:disabled_button
  • 昂瑞微:引领射频前端国产化浪潮,铸就5G时代核心竞争力
  • 基于SpringBoot的高校教师科研项目信息管理系统
  • 富文本返回的Html数据格式化
  • 昂瑞微冲刺科创板:创新驱动,引领射频芯片国产化新征程
  • 基于Java(Spring Boot)+MySQL实现电商网站