Parcourir la source

针对所有sse的接口新增Token验证机制

andyliu il y a 1 mois
Parent
commit
60d85863ad

+ 1 - 0
.cursor/rules/architecture.mdc

@@ -33,6 +33,7 @@ alwaysApply: true
     - `components/` — 组件(如自定义验证器)封装类
     - `configuration/` — 各种配置类
     - `constants/` — 全局常量定义
+    - `filters/` — 全局过滤封装类
     - `dbdao/mapper/` — 主业务数据库访问的 Mapper 定义类
     - `dbdao/services/` — 数据库访问的 DAO 服务实现
     - `dbdao/services/intef/` — DAO 服务接口定义

+ 6 - 0
pom.xml

@@ -142,6 +142,12 @@
         </dependency>
 
         <dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <version>2.3.1</version>
+        </dependency>
+
+        <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>

+ 50 - 0
src/main/java/com/shkpr/service/mcpcenterservice/commtools/JwtTokenUtil.java

@@ -0,0 +1,50 @@
+package com.shkpr.service.mcpcenterservice.commtools;
+
+import com.shkpr.service.mcpcenterservice.dto.McpAuthUser;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jwts;
+
+public class JwtTokenUtil {
+    public static final String SECRET = "TRICP_ALAM_DMA";                              // JWT密码
+    public static final String CLAIM_FLAGKEY = "flagkey";
+    public static final String CLAIM_ACCOUNT = "account";
+    public static final String CLAIM_ROLEID = "roleid";
+
+    private static Claims getClaimsFromToken(String token) throws ExpiredJwtException, Exception{
+        Claims claims = null;
+        try {
+            claims = Jwts.parser().setSigningKey(SECRET.getBytes("UTF-8")).parseClaimsJws(token).getBody();
+        }catch (ExpiredJwtException e){
+            claims = null;
+            throw e;
+        }catch (Exception e) {
+            claims = null;
+            throw e;
+        }
+        return claims;
+    }
+
+    public static McpAuthUser toAuthUser(String token) throws ExpiredJwtException, Exception{
+        McpAuthUser user = null;
+        try {
+            Claims claims = getClaimsFromToken(token);
+            if (claims != null){
+                user = McpAuthUser.builder()
+                        .userId(claims.get(CLAIM_FLAGKEY, String.class))
+                        .account(claims.get(CLAIM_ACCOUNT, String.class))
+                        .roleId(claims.get(CLAIM_ROLEID, String.class))
+                        .build();
+                user.setExpiredTm(claims.getExpiration().getTime());
+                user.setIssueTm(claims.getIssuedAt().getTime());
+            }
+        }catch (ExpiredJwtException e){
+            user = null;
+            throw e;
+        }catch (Exception e) {
+            user = null;
+            throw e;
+        }
+        return user;
+    }
+}

+ 23 - 6
src/main/java/com/shkpr/service/mcpcenterservice/configuration/McpSecurityConfig.java

@@ -1,25 +1,42 @@
 package com.shkpr.service.mcpcenterservice.configuration;
 
+import com.shkpr.service.mcpcenterservice.filters.JwtAuthenticationFilter;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.HttpStatusEntryPoint;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 
 @Configuration
 @EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled = true)
+@EnableMethodSecurity(prePostEnabled = true)
 public class McpSecurityConfig {
+    @Value("${spring.ai.mcp.server.sseEndpoint:/kpr-mcp-center/sse}")
+    private String mcpSSEPath;
+
+    @Value("${spring.ai.mcp.server.sseMessageEndpoint:/kpr-mcp-center/sse/message}")
+    private String mcpSSEMsgPath;
+
     @Bean
     public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
-        http.authorizeHttpRequests(auth -> auth
-                        .requestMatchers("/kpr-mcp-center/sse").permitAll()
-                        .requestMatchers("/kpr-mcp-center/sse/message").permitAll()
+        http
+                .csrf(csrf -> csrf.disable())
+                .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+                .authorizeHttpRequests(auth -> auth
                         .requestMatchers("/").permitAll()
+                        .requestMatchers("/actuator/health").permitAll()
+                        .requestMatchers(mcpSSEPath, mcpSSEMsgPath).authenticated()
                         .anyRequest().authenticated()
                 )
-                .csrf(csrf -> csrf.disable()); //MCP通常不需要 CSRF
+                .exceptionHandling(eh -> eh.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
+                .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
+
         return http.build();
     }
 }

+ 35 - 0
src/main/java/com/shkpr/service/mcpcenterservice/constants/ApiURI.java

@@ -0,0 +1,35 @@
+package com.shkpr.service.mcpcenterservice.constants;
+
+/**
+ * 认证相关全局常量。
+ * <p>
+ * 包括 HTTP Header 名、JWT claim 字段名、认证错误码等。
+ * 错误码与 {@code ResponseRes} 统一格式中的 {@code rescode} 对齐。
+ */
+public final class ApiURI {
+
+    private ApiURI() {
+    }
+
+    public static final String ALLOW_HEADERS = "x-requested-with,Auth-Verify,Content-Type,X-Source-IP,Biz-Type,Client-Type,Authorization,user-agent,withCredentials";
+    public static final String HEADER_AUTHORIZATION = "Authorization";
+    public static final String BEARER_PREFIX = "Bearer ";
+    public static final String QUERY_TOKEN_PARAM = "access_token";
+
+
+    public static final String ROLE_AUTHORITY_PREFIX = "ROLE_";
+
+    public static final String RESCODE_TOKEN_MISSING = "401001";
+    public static final String RESCODE_TOKEN_MALFORMED = "401002";
+    public static final String RESCODE_TOKEN_SIGNATURE_INVALID = "401003";
+    public static final String RESCODE_TOKEN_EXPIRED = "401004";
+    public static final String RESCODE_TOKEN_UNSUPPORTED = "401005";
+    public static final String RESCODE_TOKEN_PARSE_FAILED = "401099";
+
+    public static final String RESMSG_TOKEN_MISSING = "missing authorization token";
+    public static final String RESMSG_TOKEN_MALFORMED = "malformed token";
+    public static final String RESMSG_TOKEN_SIGNATURE_INVALID = "invalid token signature";
+    public static final String RESMSG_TOKEN_EXPIRED = "token expired";
+    public static final String RESMSG_TOKEN_UNSUPPORTED = "unsupported token";
+    public static final String RESMSG_TOKEN_PARSE_FAILED = "token parse failed";
+}

+ 26 - 0
src/main/java/com/shkpr/service/mcpcenterservice/dto/LogFlagBizType.java

@@ -0,0 +1,26 @@
+package com.shkpr.service.mcpcenterservice.dto;
+
+public enum LogFlagBizType {
+    BIZ_INIT(0,"Init Biz"),
+    BIZ_CONN(1,"Conn Biz"),
+    BIZ_AUTH(2, "Auth Biz"),
+    BIZ_SUB(3, "Subscribe Biz"),
+    BIZ_NOTIFY(4, "Notify Biz"),
+    BIZ_CMD(5,"Command Biz"),
+
+    BIZ_INTERNAL(99,"Internal Biz"),
+    BIZ_DB_START(100,"DB User Biz Start"),
+
+
+    BIZ_ALL(999,"ALL Biz"),
+    BIZ_REDIS(1000,"Redis Biz");
+
+    private LogFlagBizType(int index, String name){
+        this.name = name;
+        this.index = index;
+    }
+    private String name;
+    private int index;
+    public int toIntValue(){return index;}
+    public String toStrValue() {return name;}
+}

+ 18 - 0
src/main/java/com/shkpr/service/mcpcenterservice/dto/McpAuthUser.java

@@ -0,0 +1,18 @@
+package com.shkpr.service.mcpcenterservice.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class McpAuthUser {
+    private String userId = "";
+    private String account = "";
+    private String roleId = "";
+    private long expiredTm = 0L;      //Token过期时间,单位:毫秒
+    private long issueTm = 0L;        //Token签发时间,单位:毫秒
+}

+ 110 - 0
src/main/java/com/shkpr/service/mcpcenterservice/dto/ResponseCode.java

@@ -0,0 +1,110 @@
+package com.shkpr.service.mcpcenterservice.dto;
+
+public enum ResponseCode {
+    RESULT_NORMAL(0, "Normal result."),
+    RESULT_BAD(1, "Bad result,see the message/data for reason."),
+
+    STATUS_SUCCESS(200, "Success"),
+    STATUS_BAD_REQUEST(600, "Bad Request,see the message/data for reason."),
+    STATUS_ACCESS_DENY(603, "Access denied."),
+    STATUS_NOT_FOUND_URI(604, "No mapping found for HTTP request with URI."),
+    STATUS_METHOD_NOT_ALLOWED(605, "Method Not Allowed."),
+    STATUS_HTTP_TYPE_NOT_ACCEPTABLE(606, "Http media type not acceptable."),
+    STATUS_HTTP_TYPE_NOT_SUPPORTED(615, "Http media type not supported."),
+    STATUS_INTERNAL_SERVER_ERROR(700, "Internal Server Error."),
+    STATUS_SERVER_RUN_ERROR(1000,"The server is abnormal."),
+    STATUS_NULL_POINT_EXCEPTION(1001, "Empty param or param is null."),
+    STATUS_ERROR_DATA_TYPE(1002, "The data type is wrong."),
+    STATUS_IO_EXCEPTION(1003, "IO exception."),
+    STATUS_NO_SUCH_METHOD(1004, "No such method."),
+    STATUS_INDEX_OUT_OF_BOUNDS(1005, "Index out of bounds."),
+    STATUS_ERROR_JSON_FORMAT(1006, "Error JSON data format."),
+    STATUS_NOT_POST_REQUEST(1007, "Not post request."),
+    STATUS_NOT_DELETE_REQUEST(1008, "Not delete request."),
+    STATUS_ERROR_PARAM_FORMAT(1009, "Error parameter format."),
+    STATUS_INVALID_CONTENT_TYPE(1010, "Error Content-Type."),
+    STATUS_NOT_MATCHED_URI(1011, "No mapping found for HTTP request with URI."),
+    STATUS_ERROR_REQUEST_METHOD(1012, "Error request method."),
+    STATUS_NO_USER_ACCOUNT(1013, "No such user or has not login."),
+    STATUS_SERVER_NET_DISCONN_OR_BUSY(1014, "The network of server is down or busy. Please try again later."),
+    STATUS_MAX_CONCURRENT_REQUEST_COUNT(1015, "Exceeded the maximum number of concurrent requests. Please try again later."),
+    STATUS_INVALID_HEADER(1016, "Invalid headers, please check the headers of request."),
+    STATUS_INVALID_SIGN(1017, "Illegal signature, please check and request again."),
+
+    STATUS_EMPTY_TOKEN(2000,"Empty token."),
+    STATUS_INVALID_TOKEN(2001, "Invalidate Token, please re-login."),
+    STATUS_EXPIRED_TOKEN(2002, "Expired Token, please re-login."),
+    STATUS_NOT_LOGGED_IN(2003, "Not logged in."),
+    STATUS_DENY_OPERATE_USER(2004, "No permission to operate."),
+    STATUS_TOO_OLD_TOKEN(2005, "The token is too old, please use your new token."),
+    STATUS_INVALID_OLD_PWD(2006, "The original password is wrong."),
+    STATUS_INVALID_KEY(2007, "Invalidate Key"),
+    STATUS_INVALID_NAME(2008, "Invalidate Name"),
+
+
+    RESULT_ERROR_SIGN(3000, "Illegal signature"),
+    RESULT_REPEAT_SEQUENCE(3001, "Repeat the request, please check the request sequence number."),
+    RESULT_REQUEST_TIMEOUT(3002, "Request timed out."),
+    RESULT_REQUEST_TOO_BUSY(3003, "Request too busy, please try again later."),
+    RESULT_REPEAT_RECORDS(3004, "Repeat the records, please check the request."),
+    RESULT_REFERENCE_RECORDS(3005, "Reference the records, please check the request."),
+    RESULT_REFERENCE_NOT_EXIST(3006, "Reference the not exist, please check the request."),
+    RESULT_INVALID_MULTI_TYPE_BIND(3007, "Multi type binding, please check the request."),
+    RESULT_ILLEGAL_LOGIN(3998, "Illegal login."),
+    RESULT_ACCOUNT_LOCKED(3999, "Account has been locked, please try again later."),
+
+    RESULT_INVALID_ORDER(4000, "The order number is inconsistent."),//错误或不合法的工单号
+    RESULT_DUPLICATE_ORDER(4001,"The order is already exists."), //指定工单已存在
+    RESULT_ORDER_ACCEPTED(4002, "The order is already accepted."),//指定工单已被接收
+    RESULT_ORDER_DEALED(4003, "The order is already deal."),//指定工单已被处理
+    RESULT_ORDER_ODD_STATUS(4004, "The order status is incorrect.No permission to operation."),//指定工单状态错误,暂无权限做次操作
+    RESULT_ORDER_OWN_CHANGED(4005, "The owner of order is changed.No permission to operation."),//指定工单归属已变更,暂无限操作
+
+    RESULT_USER_ALREADY_REG(8000, "This phone is already registered."),
+    RESULT_DENY_FOR_SAME_PASS(8001, "Two passwords cannot be the same."),
+    RESULT_REFRESH_TOKEN_FAILED(8002, "Refresh token failed, please login again."),
+    RESULT_TOKEN_INVALID_SIGN(8003, "Refresh token failed, invalid signature"),
+    RESULT_INVALID_USER_INFO(8004, "User information verification failed."),
+    RESULT_EXISTS_USER_NAME(8005, "Username already exists."),
+    RESULT_USER_NOT_REG(8006, "This phone is not registered."),
+    RESULT_INNER_JS_SERVER_AUTH_FAILED(8007, "Inner js service verify failed."),
+
+    RESULT_SERVER_NET_DISCONN_OR_BUSY(9000, "The network of server is down or busy. Please try again later."),
+
+    RESULT_INVALID_PUSH_TASK(9100, "The push task is inconsistent."),
+    RESULT_PUSH_TASK_CLOSE(9101, "The push task will be closed."),
+    RESULT_PUSH_TASK_RETRY(9102, "The push task will be next push in the queue."),
+    RESULT_PUSH_FAILED_ERROR_RECEIVER(9103, "Push task failed because of error receiver."),
+    RESULT_PUSH_FAILED_ERROR_OTHERS(9104, "Push task failed because of other errors."),
+    RESULT_PUSH_FAILED_NO_WAY(9105, "Push task failed because of no support the push way."),
+
+    RESULT_FILE_INVALID_TYPE(10000, "Illegal file type."),
+    RESULT_FILE_RW_FILED(10001, "File read/write error."),
+    RESULT_FILE_SIZE_EXCEED(10002, "File size limit exceeded."),
+    RESULT_FILE_PARSE_FAILED(10003, "File parse failed."),
+    RESULT_FILE_EMPTY_ROWS(10004, "File is empty rows."),
+    RESULT_FILE_DATA_INVALID(10005, "File Data is invalid format."),
+
+    BUSINESS_DB_REQ_FAILED(65530,"The DB operation failed."),
+    BUSINESS_API_ABOLISH(65531,"The Api has been abolished."),
+    BUSINESS_BUSY(65532,"Business is busy, please try again."),
+    SERVICE_OFFLINE(65533, "Service offline, please check the license"),
+    STATUS_UNKNOWN(65534,"Unknown reason,see the message/data for reason."),
+    RESULT_UNKNOWN(65535,"Unknown reason,see the message/data for reason.");
+
+    private String name;
+    private int index;
+    private ResponseCode(int indexC, String strN){
+        index = indexC;
+        name = strN;
+    }
+
+    public int toInt() {return index;}
+    public String toStrCode() {return String.valueOf(index);}
+    public String toStrMsg() {return name;}
+
+    @Override
+    public String toString() {
+        return "StatusCode: " + index + " Message: " + name;
+    }
+}

+ 60 - 0
src/main/java/com/shkpr/service/mcpcenterservice/dto/ResponseRes.java

@@ -0,0 +1,60 @@
+package com.shkpr.service.mcpcenterservice.dto;
+
+public class ResponseRes<T> {
+    private long timestamp;
+    private String rescode;
+    private String resmsg;//message;
+    private T resdata;//data;
+
+    public ResponseRes() {
+    }
+
+    public ResponseRes(long timestamp, String rescode, String resmsg, T resdata) {
+        this.timestamp = timestamp;
+        this.rescode = rescode;
+        this.resmsg = resmsg;
+        this.resdata = resdata;
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public String getRescode() {
+        return rescode;
+    }
+
+    public void setRescode(String rescode) {
+        this.rescode = rescode;
+    }
+
+    public String getResmsg() {
+        return resmsg;
+    }
+
+    public void setResmsg(String resmsg) {
+        this.resmsg = resmsg;
+    }
+
+    public T getResdata() {
+        return resdata;
+    }
+
+    public void setResdata(T resdata) {
+        this.resdata = resdata;
+    }
+
+    @Override
+    public String toString() {
+        return "ResponseRes{" +
+                "timestamp=" + timestamp +
+                ", rescode='" + rescode + '\'' +
+                ", resmsg='" + resmsg + '\'' +
+                ", resdata=" + resdata +
+                '}';
+    }
+}

+ 135 - 0
src/main/java/com/shkpr/service/mcpcenterservice/filters/JwtAuthenticationFilter.java

@@ -0,0 +1,135 @@
+package com.shkpr.service.mcpcenterservice.filters;
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.global.base.tools.FastJsonUtil;
+import com.shkpr.service.mcpcenterservice.commtools.JwtTokenUtil;
+import com.shkpr.service.mcpcenterservice.constants.ApiURI;
+import com.shkpr.service.mcpcenterservice.dto.LogFlagBizType;
+import com.shkpr.service.mcpcenterservice.dto.McpAuthUser;
+import com.shkpr.service.mcpcenterservice.dto.ResponseCode;
+import com.shkpr.service.mcpcenterservice.dto.ResponseRes;
+import com.shkpr.service.mcpcenterservice.globalmgr.McpAuthContextMgr;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+import java.util.List;
+
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+    private String mStrClassName = "";
+    private String mBusinessType = "";
+    private String logTag = "";
+    public JwtAuthenticationFilter() {
+        mStrClassName = this.getClass().getSimpleName();
+        mBusinessType = LogFlagBizType.BIZ_AUTH.toStrValue();
+        logTag = "Biz Auth";
+    }
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+        String token = extractToken(request);
+        if (StringUtils.isEmpty(token)) {
+            writeAuthError(response, ResponseCode.STATUS_EMPTY_TOKEN);
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_WARN, mBusinessType, mStrClassName
+                    , String.format("Empty Token, Uri{%s} Remote{%s:%d}"
+                            ,request.getRequestURI()
+                            ,request.getRemoteAddr()
+                            ,request.getRemotePort()));
+            return;
+        }
+
+        McpAuthUser user = null;
+        try {
+            user = JwtTokenUtil.toAuthUser(token);
+            List<SimpleGrantedAuthority> authorities = StringUtils.isEmpty(user.getRoleId())
+                    ? List.of() : List.of(new SimpleGrantedAuthority(ApiURI.ROLE_AUTHORITY_PREFIX + user.getRoleId()));
+
+            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, authorities);
+            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+            SecurityContextHolder.getContext().setAuthentication(authentication);
+            McpAuthContextMgr.set(token, user);
+        } catch (Exception ex) {
+            SecurityContextHolder.clearContext();
+            McpAuthContextMgr.clear();
+            writeAuthError(response, ResponseCode.STATUS_INVALID_TOKEN);
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_WARN, mBusinessType, mStrClassName
+                    , String.format("Token Parse Failed, Uri{%s} Remote{%s:%d}"
+                            ,request.getRequestURI()
+                            ,request.getRemoteAddr()
+                            ,request.getRemotePort()));
+            return;
+        }
+
+        if (user == null){
+            SecurityContextHolder.clearContext();
+            McpAuthContextMgr.clear();
+            writeAuthError(response, ResponseCode.STATUS_INVALID_TOKEN);
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_WARN, mBusinessType, mStrClassName
+                    , String.format("Invalid Token, Uri{%s} Remote{%s:%d}"
+                            ,request.getRequestURI()
+                            ,request.getRemoteAddr()
+                            ,request.getRemotePort()));
+            return;
+        }else {
+            if (user.getExpiredTm() < System.currentTimeMillis()){
+                SecurityContextHolder.clearContext();
+                McpAuthContextMgr.clear();
+                writeAuthError(response, ResponseCode.STATUS_EXPIRED_TOKEN);
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_WARN, mBusinessType, mStrClassName
+                        , String.format("Token Expired, Uri{%s} Remote{%s:%d}"
+                                ,request.getRequestURI()
+                                ,request.getRemoteAddr()
+                                ,request.getRemotePort()));
+                return;
+            }
+        }
+
+        try {
+            filterChain.doFilter(request, response);
+        } finally {
+            McpAuthContextMgr.clear();
+            SecurityContextHolder.clearContext();
+        }
+    }
+
+    private String extractToken(HttpServletRequest request) {
+        String header = request.getHeader(ApiURI.HEADER_AUTHORIZATION);
+        if (!StringUtils.isEmpty(header))
+            return header.trim();
+
+        String queryToken = request.getParameter(ApiURI.QUERY_TOKEN_PARAM);
+        if (!StringUtils.isEmpty(queryToken))
+            return queryToken.trim();
+        return "";
+    }
+
+    private void writeAuthError(HttpServletResponse response, ResponseCode code) throws IOException {
+        response.setHeader("Access-Control-Allow-Origin", "*");
+        response.setHeader("Access-Control-Allow-Methods", "*");
+        response.setHeader("Access-Control-Allow-Headers", ApiURI.ALLOW_HEADERS);
+        response.setStatus(HttpStatus.UNAUTHORIZED.value());
+        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
+
+        ResponseRes<String> resResult = new ResponseRes<String>();
+        resResult.setRescode(code.toStrCode());
+        resResult.setResmsg(code.toStrMsg());
+        resResult.setResdata(code.toString());
+        resResult.setTimestamp(System.currentTimeMillis());
+        try {
+            response.getWriter().write(FastJsonUtil.toJSON(resResult));
+            response.getWriter().flush();
+        }catch (Exception e){
+        }
+    }
+}

+ 28 - 0
src/main/java/com/shkpr/service/mcpcenterservice/globalmgr/McpAuthContextMgr.java

@@ -0,0 +1,28 @@
+package com.shkpr.service.mcpcenterservice.globalmgr;
+
+import com.shkpr.service.mcpcenterservice.dto.McpAuthUser;
+
+public final class McpAuthContextMgr {
+    private static final ThreadLocal<String> TOKEN_HOLDER = new ThreadLocal<>();
+    private static final ThreadLocal<McpAuthUser> USER_HOLDER = new ThreadLocal<>();
+
+    private McpAuthContextMgr() {}
+
+    public static void set(String rawToken, McpAuthUser user) {
+        TOKEN_HOLDER.set(rawToken);
+        USER_HOLDER.set(user);
+    }
+
+    public static String getCurrentToken() {
+        return TOKEN_HOLDER.get();
+    }
+
+    public static McpAuthUser getCurrentUser() {
+        return USER_HOLDER.get();
+    }
+
+    public static void clear() {
+        TOKEN_HOLDER.remove();
+        USER_HOLDER.remove();
+    }
+}

+ 18 - 0
src/main/java/com/shkpr/service/mcpcenterservice/mcptool/DateMcpTool.java

@@ -1,5 +1,8 @@
 package com.shkpr.service.mcpcenterservice.mcptool;
 
+import com.global.base.tools.FastJsonUtil;
+import com.shkpr.service.mcpcenterservice.dto.McpAuthUser;
+import com.shkpr.service.mcpcenterservice.globalmgr.McpAuthContextMgr;
 import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
 import io.modelcontextprotocol.spec.McpSchema.TextContent;
 import org.springaicommunity.mcp.annotation.McpTool;
@@ -66,4 +69,19 @@ public class DateMcpTool {
     public String today() {
         return LocalDate.now().toString();
     }
+
+    @McpTool(name = "myUser", description = "Returns current user info.")
+    public CallToolResult myUser() {
+        McpAuthUser user = McpAuthContextMgr.getCurrentUser();
+        if (user == null) {
+            return new CallToolResult(
+                    List.of(new TextContent("未检查到Token令牌")),
+                    true
+            );
+        }
+        return new CallToolResult(
+                List.of(new TextContent(FastJsonUtil.toJSON(user))),
+                false
+        );
+    }
 }