feat: 新增ai设备推送自动过鱼设施鉴权接口
This commit is contained in:
parent
46902f2a7c
commit
a3589c7ceb
@ -0,0 +1,161 @@
|
|||||||
|
package com.yfd.platform.config;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用 Bearer Token 鉴权过滤器
|
||||||
|
*
|
||||||
|
* 通过 application.yml 配置需要保护的接口路径,支持 Ant 风格匹配。
|
||||||
|
*
|
||||||
|
* <p>配置示例:
|
||||||
|
* <pre>
|
||||||
|
* bearer-token:
|
||||||
|
* paths:
|
||||||
|
* - /fb/fishhatchrecr/**
|
||||||
|
* - /fprd/wva/**
|
||||||
|
* - /some/api/**
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>外部系统调用流程:
|
||||||
|
* <ol>
|
||||||
|
* <li>先调用 OAuth2 token 端点获取 access_token</li>
|
||||||
|
* <li>在业务请求中携带 Authorization: Bearer <access_token></li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class BearerTokenFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(BearerTokenFilter.class);
|
||||||
|
|
||||||
|
private final AntPathMatcher pathMatcher = new AntPathMatcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需要 Bearer Token 鉴权保护的路径模式列表
|
||||||
|
* 支持 Ant 风格:/api/**, /fb/* /GetKendoListCust
|
||||||
|
*/
|
||||||
|
private final List<String> protectedPathPatterns = new ArrayList<>();
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private BearerTokenService bearerTokenService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private BearerTokenProperties bearerTokenProperties;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
String requestPath = resolveRequestPath(request);
|
||||||
|
|
||||||
|
// 检查当前请求路径是否需要 Bearer Token 鉴权
|
||||||
|
if (!isProtectedPath(requestPath)) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("请求路径 {} 需要 Bearer Token 鉴权", requestPath);
|
||||||
|
|
||||||
|
// 解析 Authorization: Bearer <token>
|
||||||
|
String token = bearerTokenService.resolveToken(request);
|
||||||
|
if (token == null) {
|
||||||
|
log.warn("缺少 Bearer Token, path={}", requestPath);
|
||||||
|
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "缺少 Authorization: Bearer <token> 请求头");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证 token
|
||||||
|
if (!bearerTokenService.validateToken(token, request)) {
|
||||||
|
log.warn("Bearer Token 无效或已过期, path={}", requestPath);
|
||||||
|
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Bearer Token 无效或已过期");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// token 有效,可选:刷新 token 有效期
|
||||||
|
// bearerTokenService.refreshToken(token);
|
||||||
|
|
||||||
|
// 设置 Spring Security 认证上下文,放行后续过滤器
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken =
|
||||||
|
new UsernamePasswordAuthenticationToken(
|
||||||
|
"bearer-access",
|
||||||
|
null,
|
||||||
|
AuthorityUtils.NO_AUTHORITIES
|
||||||
|
);
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
|
||||||
|
|
||||||
|
log.debug("Bearer Token 鉴权通过, path={}", requestPath);
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断路径是否需要 Bearer Token 鉴权保护
|
||||||
|
*/
|
||||||
|
private boolean isProtectedPath(String uri) {
|
||||||
|
if (uri == null) return false;
|
||||||
|
|
||||||
|
List<String> patterns = getProtectedPaths();
|
||||||
|
if (patterns.isEmpty()) return false;
|
||||||
|
|
||||||
|
for (String pattern : patterns) {
|
||||||
|
if (pathMatcher.match(pattern, uri)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取受保护的路径模式列表
|
||||||
|
* 优先从 BearerTokenProperties 获取,也可以通过子类覆盖或动态添加
|
||||||
|
*/
|
||||||
|
protected List<String> getProtectedPaths() {
|
||||||
|
List<String> paths = new ArrayList<>();
|
||||||
|
// 从配置文件加载
|
||||||
|
if (bearerTokenProperties != null && bearerTokenProperties.getPaths() != null) {
|
||||||
|
paths.addAll(bearerTokenProperties.getPaths());
|
||||||
|
}
|
||||||
|
// 合并代码中动态添加的路径
|
||||||
|
paths.addAll(protectedPathPatterns);
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 程序化添加受保护路径(Ant 风格匹配)
|
||||||
|
*
|
||||||
|
* @param pattern Ant 路径模式,如 /fb/fishhatchrecr/**
|
||||||
|
*/
|
||||||
|
public void addProtectedPath(String pattern) {
|
||||||
|
if (pattern != null && !protectedPathPatterns.contains(pattern)) {
|
||||||
|
protectedPathPatterns.add(pattern);
|
||||||
|
log.info("已添加 Bearer Token 保护路径: {}", pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveRequestPath(HttpServletRequest request) {
|
||||||
|
String servletPath = request.getServletPath();
|
||||||
|
if (servletPath != null && !servletPath.isEmpty()) {
|
||||||
|
return servletPath;
|
||||||
|
}
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
String contextPath = request.getContextPath();
|
||||||
|
if (contextPath != null && !contextPath.isEmpty() && uri.startsWith(contextPath)) {
|
||||||
|
return uri.substring(contextPath.length());
|
||||||
|
}
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.yfd.platform.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bearer Token 鉴权配置属性
|
||||||
|
*
|
||||||
|
* 在 application.yml 中配置需要保护的接口路径:
|
||||||
|
* <pre>
|
||||||
|
* bearer-token:
|
||||||
|
* paths:
|
||||||
|
* - /fb/fishhatchrecr/**
|
||||||
|
* - /some/other/api/**
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "bearer-token")
|
||||||
|
public class BearerTokenProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需要使用 Bearer Token 鉴权的接口路径列表
|
||||||
|
* 支持 Ant 风格路径匹配,如 /api/**, /fb/**
|
||||||
|
*/
|
||||||
|
private List<String> paths = new ArrayList<>(List.of("/base/fpssrlR/ai/report"));
|
||||||
|
}
|
||||||
@ -0,0 +1,189 @@
|
|||||||
|
package com.yfd.platform.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bearer Token 鉴权服务
|
||||||
|
*
|
||||||
|
* 负责 Bearer Token 的生成、缓存和验证。
|
||||||
|
* Token 通过 OAuth2 client_credentials 模式下发后,由本服务统一验证。
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class BearerTokenService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(BearerTokenService.class);
|
||||||
|
|
||||||
|
/** 默认 token 有效期:2 小时 */
|
||||||
|
public static final long DEFAULT_EXPIRE_MILLIS = 2 * 60 * 60 * 1000L;
|
||||||
|
|
||||||
|
private static final String CACHE_PREFIX = "bearer:token:";
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private WebConfig webConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成 Bearer Token 并存入缓存
|
||||||
|
* 由 OAuth2 token 端点调用
|
||||||
|
*
|
||||||
|
* @param request 当前请求
|
||||||
|
* @param clientId 客户端标识
|
||||||
|
* @return token 信息(accessToken, expiresIn 等)
|
||||||
|
*/
|
||||||
|
public BearerTokenResult createToken(HttpServletRequest request, String clientId) {
|
||||||
|
String token = UUID.randomUUID().toString().replace("-", "");
|
||||||
|
long expireAt = System.currentTimeMillis() + DEFAULT_EXPIRE_MILLIS;
|
||||||
|
|
||||||
|
JSONObject payload = new JSONObject();
|
||||||
|
payload.put("clientId", clientId);
|
||||||
|
payload.put("ip", getClientIp(request));
|
||||||
|
payload.put("expireAt", expireAt);
|
||||||
|
payload.put("createTime", System.currentTimeMillis());
|
||||||
|
|
||||||
|
webConfig.loginuserCache().put(CACHE_PREFIX + token, payload.toJSONString(), DEFAULT_EXPIRE_MILLIS);
|
||||||
|
|
||||||
|
log.info("Bearer Token 已创建, clientId={}, expireAt={}", clientId, expireAt);
|
||||||
|
|
||||||
|
BearerTokenResult result = new BearerTokenResult();
|
||||||
|
result.setAccessToken(token);
|
||||||
|
result.setTokenType("bearer");
|
||||||
|
result.setExpiresIn(DEFAULT_EXPIRE_MILLIS / 1000);
|
||||||
|
result.setExpireAt(expireAt);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证 Bearer Token 是否有效
|
||||||
|
*
|
||||||
|
* @param token Bearer Token
|
||||||
|
* @param request 当前请求
|
||||||
|
* @return true 有效,false 无效或过期
|
||||||
|
*/
|
||||||
|
public boolean validateToken(String token, HttpServletRequest request) {
|
||||||
|
if (StrUtil.isBlank(token)) {
|
||||||
|
log.debug("Bearer Token 验证失败: token 为空");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String cacheValue = webConfig.loginuserCache().get(CACHE_PREFIX + token);
|
||||||
|
if (StrUtil.isBlank(cacheValue)) {
|
||||||
|
log.debug("Bearer Token 验证失败: token 不存在或已过期, token={}", maskToken(token));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject payload;
|
||||||
|
try {
|
||||||
|
payload = JSON.parseObject(cacheValue);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Bearer Token 解析失败: {}", e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查过期时间
|
||||||
|
Long expireAt = payload.getLong("expireAt");
|
||||||
|
if (expireAt == null || expireAt < System.currentTimeMillis()) {
|
||||||
|
log.debug("Bearer Token 已过期, token={}", maskToken(token));
|
||||||
|
webConfig.loginuserCache().remove(CACHE_PREFIX + token);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选:校验 IP 一致性(可在子类中覆盖)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从请求中解析 Bearer Token
|
||||||
|
* 从 Authorization: Bearer <token> 头中提取
|
||||||
|
*
|
||||||
|
* @param request HTTP 请求
|
||||||
|
* @return token 字符串,解析失败返回 null
|
||||||
|
*/
|
||||||
|
public String resolveToken(HttpServletRequest request) {
|
||||||
|
String authHeader = request.getHeader("Authorization");
|
||||||
|
if (StrUtil.isBlank(authHeader)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持 "Bearer xxx" 和 "bearer xxx" 格式
|
||||||
|
String trimmed = authHeader.trim();
|
||||||
|
if (trimmed.length() > 7
|
||||||
|
&& "bearer".equalsIgnoreCase(trimmed.substring(0, 6))
|
||||||
|
&& trimmed.charAt(6) == ' ') {
|
||||||
|
return trimmed.substring(7).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新 token 有效期(延长使用时间)
|
||||||
|
*
|
||||||
|
* @param token Bearer Token
|
||||||
|
*/
|
||||||
|
public void refreshToken(String token) {
|
||||||
|
if (StrUtil.isBlank(token)) return;
|
||||||
|
String cacheValue = webConfig.loginuserCache().get(CACHE_PREFIX + token);
|
||||||
|
if (StrUtil.isBlank(cacheValue)) return;
|
||||||
|
|
||||||
|
JSONObject payload = JSON.parseObject(cacheValue);
|
||||||
|
long newExpireAt = System.currentTimeMillis() + DEFAULT_EXPIRE_MILLIS;
|
||||||
|
payload.put("expireAt", newExpireAt);
|
||||||
|
webConfig.loginuserCache().put(CACHE_PREFIX + token, payload.toJSONString(), DEFAULT_EXPIRE_MILLIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤销 token
|
||||||
|
*
|
||||||
|
* @param token Bearer Token
|
||||||
|
*/
|
||||||
|
public void revokeToken(String token) {
|
||||||
|
if (StrUtil.isNotBlank(token)) {
|
||||||
|
webConfig.loginuserCache().remove(CACHE_PREFIX + token);
|
||||||
|
log.info("Bearer Token 已撤销");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getClientIp(HttpServletRequest request) {
|
||||||
|
String ip = request.getHeader("X-Forwarded-For");
|
||||||
|
if (StrUtil.isNotBlank(ip)) {
|
||||||
|
return ip.split(",")[0].trim();
|
||||||
|
}
|
||||||
|
return request.getRemoteAddr();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String maskToken(String token) {
|
||||||
|
if (token == null || token.length() <= 8) return "***";
|
||||||
|
return token.substring(0, 4) + "***" + token.substring(token.length() - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bearer Token 创建结果
|
||||||
|
*/
|
||||||
|
public static class BearerTokenResult {
|
||||||
|
/** 访问令牌 */
|
||||||
|
private String accessToken;
|
||||||
|
/** 令牌类型 */
|
||||||
|
private String tokenType;
|
||||||
|
/** 有效期(秒) */
|
||||||
|
private long expiresIn;
|
||||||
|
/** 过期时间戳 */
|
||||||
|
private long expireAt;
|
||||||
|
|
||||||
|
public String getAccessToken() { return accessToken; }
|
||||||
|
public void setAccessToken(String accessToken) { this.accessToken = accessToken; }
|
||||||
|
public String getTokenType() { return tokenType; }
|
||||||
|
public void setTokenType(String tokenType) { this.tokenType = tokenType; }
|
||||||
|
public long getExpiresIn() { return expiresIn; }
|
||||||
|
public void setExpiresIn(long expiresIn) { this.expiresIn = expiresIn; }
|
||||||
|
public long getExpireAt() { return expireAt; }
|
||||||
|
public void setExpireAt(long expireAt) { this.expireAt = expireAt; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
package com.yfd.platform.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.yfd.platform.config.BearerTokenService.BearerTokenResult;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth2 Token 端点
|
||||||
|
*
|
||||||
|
* <p>供外部系统通过 client_credentials 模式获取 Bearer Token。
|
||||||
|
*
|
||||||
|
* <p>请求格式(参考 鉴权与文件上传接口说明.md 3.2 节):
|
||||||
|
* <pre>
|
||||||
|
* POST /oauth2/token
|
||||||
|
* Content-Type: application/x-www-form-urlencoded
|
||||||
|
* Authorization: Basic <base64(clientId:clientSecret)>
|
||||||
|
*
|
||||||
|
* grant_type=client_credentials
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>返回格式:
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "success": true,
|
||||||
|
* "errorCode": 0,
|
||||||
|
* "message": "获取token成功",
|
||||||
|
* "data": {
|
||||||
|
* "access_token": "xxx",
|
||||||
|
* "token_type": "bearer",
|
||||||
|
* "expires_in": 7200
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/oauth2")
|
||||||
|
public class OAuth2TokenController {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(OAuth2TokenController.class);
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private BearerTokenService bearerTokenService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 Access Token(client_credentials 模式)
|
||||||
|
*/
|
||||||
|
@PostMapping("/oauth/token")
|
||||||
|
public Map<String, Object> getToken(HttpServletRequest request) {
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
|
||||||
|
// 解析 Authorization: Basic <base64>
|
||||||
|
String authHeader = request.getHeader("Authorization");
|
||||||
|
if (StrUtil.isBlank(authHeader) || !authHeader.trim().toLowerCase().startsWith("basic ")) {
|
||||||
|
log.warn("获取 Token 失败: 缺少有效的 Basic Authorization 头");
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("errorCode", -1);
|
||||||
|
result.put("message", "缺少有效的 Authorization: Basic 认证信息");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
String clientId = resolveClientId(authHeader);
|
||||||
|
if (StrUtil.isBlank(clientId)|| !"client".equals(clientId)) {
|
||||||
|
log.warn("获取 Token 失败: 无法解析 client");
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("errorCode", -1);
|
||||||
|
result.put("message", "无法解析客户端标识");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证 grant_type
|
||||||
|
String grantType = request.getParameter("grant_type");
|
||||||
|
if (StrUtil.isBlank(grantType) || !"client_credentials".equals(grantType)) {
|
||||||
|
log.warn("获取 Token 失败: 不支持的 grant_type={}", grantType);
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("errorCode", -1);
|
||||||
|
result.put("message", "仅支持 client_credentials 授权模式");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BearerTokenResult tokenResult = bearerTokenService.createToken(request, clientId);
|
||||||
|
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("access_token", tokenResult.getAccessToken());
|
||||||
|
data.put("token_type", tokenResult.getTokenType());
|
||||||
|
data.put("expires_in", tokenResult.getExpiresIn());
|
||||||
|
|
||||||
|
result.put("success", true);
|
||||||
|
result.put("errorCode", 0);
|
||||||
|
result.put("message", "获取token成功");
|
||||||
|
result.put("data", data);
|
||||||
|
|
||||||
|
log.info("OAuth2 Token 签发成功, clientId={}", clientId);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 Authorization: Basic <base64> 头中解析 client
|
||||||
|
*/
|
||||||
|
private String resolveClientId(String authHeader) {
|
||||||
|
try {
|
||||||
|
String base64 = authHeader.substring(6).trim();
|
||||||
|
byte[] decoded = Base64.getDecoder().decode(base64);
|
||||||
|
String credentials = new String(decoded, StandardCharsets.UTF_8);
|
||||||
|
// 格式: clientId:clientSecret
|
||||||
|
int colonIdx = credentials.indexOf(':');
|
||||||
|
if (colonIdx > 0) {
|
||||||
|
return credentials.substring(0, colonIdx);
|
||||||
|
}
|
||||||
|
return credentials;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("解析 Basic 认证信息失败: {}", e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -42,6 +42,9 @@ public class SecurityConfig {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private RegisterAccessTokenFilter registerAccessTokenFilter;
|
private RegisterAccessTokenFilter registerAccessTokenFilter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BearerTokenFilter bearerTokenFilter;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AuthenticationException authenticationException;
|
private AuthenticationException authenticationException;
|
||||||
|
|
||||||
@ -60,7 +63,7 @@ public class SecurityConfig {
|
|||||||
.requestMatchers("/data/fishDraft/previewFile").permitAll()
|
.requestMatchers("/data/fishDraft/previewFile").permitAll()
|
||||||
.requestMatchers("/tempFile/**").permitAll()
|
.requestMatchers("/tempFile/**").permitAll()
|
||||||
.requestMatchers("/system/user/auditUser").permitAll()
|
.requestMatchers("/system/user/auditUser").permitAll()
|
||||||
.requestMatchers("/register/accessToken").permitAll()
|
.requestMatchers("/api/oauth2/oauth/token").permitAll()
|
||||||
.requestMatchers("/sys/psbmodulelbb/**").permitAll()
|
.requestMatchers("/sys/psbmodulelbb/**").permitAll()
|
||||||
.requestMatchers("/base/operationLog/**").permitAll()
|
.requestMatchers("/base/operationLog/**").permitAll()
|
||||||
// .requestMatchers("/eng/**").permitAll()
|
// .requestMatchers("/eng/**").permitAll()
|
||||||
@ -114,6 +117,7 @@ public class SecurityConfig {
|
|||||||
.cors(cors -> {});
|
.cors(cors -> {});
|
||||||
|
|
||||||
http.addFilterBefore(registerAccessTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(registerAccessTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
http.addFilterBefore(bearerTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
http.exceptionHandling(ex -> ex
|
http.exceptionHandling(ex -> ex
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user