1.前端发送请求
前端发送请求时有一个全局拦截器,拦截所有请求并配置 Authorization 头部信息,配置token,一共给后台鉴权
| |
| service.interceptors.request.use( |
| config => { |
| if (!permission.access(config, store)) { |
| |
| throw { |
| type: '403', |
| config: config |
| } |
| } |
| loading.show(config) |
| |
| if (!(/^https:\/\/|http:\/\//.test(config.url))) { |
| const token = util.cookies.get('token') |
| if (token && token !== 'undefined') { |
| |
| config.headers['Authorization'] = 'Bearer ' + token |
| } |
| } |
| return config |
| }, |
| error => { |
| |
| console.log(error) |
| Promise.reject(error) |
| } |
| ) |
2.后台全局网关过滤器拦截请求
| @Configuration |
| @Slf4j |
| public class AccessGatewayFilter implements GlobalFilter { |
| |
| @Override |
| public Mono<Void> filter(ServerWebExchange serverWebExchange, GatewayFilterChain gatewayFilterChain) { |
| log.info("check token and user permission...."); |
| |
| LinkedHashSet requiredAttribute = serverWebExchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR); |
| ServerHttpRequest request = serverWebExchange.getRequest(); |
| |
| String requestUri = request.getPath().pathWithinApplication().value(); |
| if (requiredAttribute != null) { |
| Iterator<URI> iterator = requiredAttribute.iterator(); |
| while (iterator.hasNext()) { |
| URI next = iterator.next(); |
| if (next.getPath().startsWith(GATE_WAY_PREFIX)) { |
| |
| requestUri = next.getPath().substring(GATE_WAY_PREFIX.length()); |
| } |
| } |
| } |
| final String method = request.getMethod().toString(); |
| BaseContextHandler.setToken(null); |
| ServerHttpRequest.Builder mutate = request.mutate(); |
| |
| if (isStartWith(requestUri)) { |
| ServerHttpRequest build = mutate.build(); |
| return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build()); |
| } |
| IJWTInfo user = null; |
| try { |
| |
| user = getJWTUser(request, mutate); |
| } catch (Exception e) { |
| log.error("用户Token过期异常", e); |
| return getVoidMono(serverWebExchange, new TokenForbiddenResponse("User Token Error or Expired!"), HttpStatus.UNAUTHORIZED); |
| } |
| } |
| |
| private boolean isStartWith(String requestUri) { |
| boolean flag = false; |
| for (String s : startWith.split(",")) { |
| if (requestUri.startsWith(s)) { |
| return true; |
| } |
| } |
| return flag; |
| } |
3.JWT 鉴权并解析用户信息
| private IJWTInfo getJWTUser(ServerHttpRequest request, ServerHttpRequest.Builder ctx) throws Exception { |
| List<String> strings = request.getHeaders().get(userAuthConfig.getTokenHeader()); |
| String authToken = null; |
| if (strings != null) { |
| authToken = strings.get(0); |
| } |
| if (StringUtils.isBlank(authToken)) { |
| strings = request.getQueryParams().get("token"); |
| if (strings != null) { |
| authToken = strings.get(0); |
| } |
| } |
| IJWTInfo infoFromToken = userAuthUtil.getInfoFromToken(authToken); |
| |
| String s = stringRedisTemplate.opsForValue().get(RedisKeyConstant.REDIS_KEY_TOKEN + ":" + infoFromToken.getTokenId()); |
| if (StringUtils.isBlank(s)) { |
| throw new UserTokenException("User token expired!"); |
| } |
| ctx.header(userAuthConfig.getTokenHeader(), authToken); |
| BaseContextHandler.setToken(authToken); |
| return infoFromToken; |
| } |
使用工具类解析 token
| public static IJWTInfo getInfoFromToken(String token, byte[] pubKey) throws Exception { |
| if(token.startsWith("Bearer")){ |
| token = token.replace("Bearer ",""); |
| } |
| Jws<Claims> claimsJws = parserToken(token, pubKey); |
| Claims body = claimsJws.getBody(); |
| return new JWTInfo(body.getSubject(), StringHelper.getObjectValue(body.get(CommonConstants.JWT_KEY_USER_ID)), StringHelper.getObjectValue(body.get(CommonConstants.JWT_KEY_NAME)),StringHelper.getObjectValue(body.get(CommonConstants.JWT_ID))); |
| } |
JWT 工具类
| public class JWTHelper { |
| private static RsaKeyHelper rsaKeyHelper = new RsaKeyHelper(); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static String generateToken(IJWTInfo jwtInfo, String priKeyPath, int expire) throws Exception { |
| String compactJws = Jwts.builder() |
| .setSubject(jwtInfo.getUniqueName()) |
| .claim(CommonConstants.JWT_KEY_USER_ID, jwtInfo.getId()) |
| .claim(CommonConstants.JWT_KEY_NAME, jwtInfo.getName()) |
| .setExpiration(DateTime.now().plusSeconds(expire).toDate()) |
| .signWith(SignatureAlgorithm.RS256, rsaKeyHelper.getPrivateKey(priKeyPath)) |
| .compact(); |
| return compactJws; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static String generateToken(IJWTInfo jwtInfo, byte priKey[], int expire) throws Exception { |
| String compactJws = Jwts.builder() |
| .setSubject(jwtInfo.getUniqueName()) |
| .claim(CommonConstants.JWT_KEY_USER_ID, jwtInfo.getId()) |
| .claim(CommonConstants.JWT_KEY_NAME, jwtInfo.getName()) |
| .claim(CommonConstants.JWT_ID, jwtInfo.getTokenId()) |
| .setExpiration(DateTime.now().plusSeconds(expire).toDate()) |
| .signWith(SignatureAlgorithm.RS256, rsaKeyHelper.getPrivateKey(priKey)) |
| .compact(); |
| return compactJws; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| public static Jws<Claims> parserToken(String token, String pubKeyPath) throws Exception { |
| Jws<Claims> claimsJws = Jwts.parser().setSigningKey(rsaKeyHelper.getPublicKey(pubKeyPath)).parseClaimsJws(token); |
| return claimsJws; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static Jws<Claims> parserToken(String token, byte[] pubKey) throws Exception { |
| Jws<Claims> claimsJws = Jwts.parser().setSigningKey(rsaKeyHelper.getPublicKey(pubKey)).parseClaimsJws(token); |
| return claimsJws; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| public static IJWTInfo getInfoFromToken(String token, String pubKeyPath) throws Exception { |
| Jws<Claims> claimsJws = parserToken(token, pubKeyPath); |
| Claims body = claimsJws.getBody(); |
| return new JWTInfo(body.getSubject(), StringHelper.getObjectValue(body.get(CommonConstants.JWT_KEY_USER_ID)), StringHelper.getObjectValue(body.get(CommonConstants.JWT_KEY_NAME))); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| public static IJWTInfo getInfoFromToken(String token, byte[] pubKey) throws Exception { |
| if(token.startsWith("Bearer")){ |
| token = token.replace("Bearer ",""); |
| } |
| Jws<Claims> claimsJws = parserToken(token, pubKey); |
| Claims body = claimsJws.getBody(); |
| return new JWTInfo(body.getSubject(), StringHelper.getObjectValue(body.get(CommonConstants.JWT_KEY_USER_ID)), StringHelper.getObjectValue(body.get(CommonConstants.JWT_KEY_NAME)),StringHelper.getObjectValue(body.get(CommonConstants.JWT_ID))); |
| } |
| } |
| |
4.检查权限
| Mono<CheckPermissionInfo> checkPermissionInfoMono = webClientBuilder.build(). |
| get().uri("http://ace-admin/api/user/{username}/check_permission?requestMethod=" + method + "&requestUri=" + requestUri, user.getUniqueName()).header(userAuthConfig.getTokenHeader(), BaseContextHandler.getToken()).retrieve().bodyToMono(CheckPermissionInfo.class); |
| |
| IJWTInfo finalUser = user; |
| return checkPermissionInfoMono.flatMap(checkPermissionInfo -> { |
| |
| if (checkPermissionInfo.getIsAuth()) { |
| if (checkPermissionInfo.getPermissionInfo() != null) { |
| |
| setCurrentUserInfoAndLog(serverWebExchange, finalUser, checkPermissionInfo.getPermissionInfo()); |
| } |
| ServerHttpRequest build = mutate.build(); |
| return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build()); |
| } else { |
| |
| return getVoidMono(serverWebExchange, new TokenForbiddenResponse("Forbidden!Does not has Permission!"), HttpStatus.FORBIDDEN); |
| } |
| }); |
获取所有权限控制
| public List<PermissionInfo> getAllPermission() { |
| String key = RedisKeyConstant.REDIS_KEY_ALL_PERMISISON; |
| String s = stringRedisTemplate.opsForValue().get(key); |
| if (s == null || org.apache.commons.lang.StringUtils.isBlank(s)) { |
| List<Menu> menus = menuBiz.selectListAll(); |
| List<PermissionInfo> result = new ArrayList<PermissionInfo>(); |
| menu2permission(menus, result); |
| List<Element> elements = elementBiz.getAllElementPermissions(); |
| element2permission(result, elements); |
| s = JSON.toJSONString(result); |
| stringRedisTemplate.opsForValue().set(key, s, 12, TimeUnit.HOURS); |
| } |
| List<PermissionInfo> permissionInfos = JSON.parseArray(s, PermissionInfo.class); |
| return permissionInfos; |
| } |
判断当前请求是否需要做权限控制
| |
| List<PermissionInfo> matchPermission = allPermission.parallelStream().filter(permissionInfo -> { |
| String uri = permissionInfo.getUri(); |
| if (uri.indexOf("{") > 0) { |
| uri = uri.replaceAll("\\{\\*\\}", "[a-zA-Z\\\\d]+"); |
| } |
| String regEx = "^" + uri + "$"; |
| return (Pattern.compile(regEx).matcher(requestUri).find()) |
| && requestMethod.equals(permissionInfo.getMethod()); |
| }).collect(Collectors.toList()); |
| |
| if (matchPermission.size() == 0) { |
| checkPermissionInfo.setIsAuth(true); |
| return Mono.just(checkPermissionInfo); |
| } |
获取当前用户拥有的权限
| public List<PermissionInfo> getPermissionByUsername(String username) { |
| String key = String.format(RedisKeyConstant.REDIS_KEY_USER_PERMISISON, username); |
| String s = stringRedisTemplate.opsForValue().get(key); |
| if (s == null || org.apache.commons.lang.StringUtils.isBlank(s)) { |
| User user = userBiz.getUserByUsername(username); |
| List<Menu> menus = menuBiz.getUserAuthorityMenuByUserId(user.getId()); |
| List<PermissionInfo> result = new ArrayList<PermissionInfo>(); |
| menu2permission(menus, result); |
| List<Element> elements = elementBiz.getAuthorityElementByUserId(user.getId() + ""); |
| element2permission(result, elements); |
| stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(result), 12, TimeUnit.HOURS); |
| |
| return result; |
| } |
| List<PermissionInfo> permissionInfos = JSON.parseArray(s, PermissionInfo.class); |
| return permissionInfos; |
| } |
判断用户拥有的权限里面有没有当前请求需要的权限
| |
| List<PermissionInfo> permissions = this.getPermissionByUsername(username); |
| PermissionInfo current = null; |
| for (PermissionInfo info : permissions) { |
| |
| boolean anyMatch = matchPermission.parallelStream().anyMatch(permissionInfo -> permissionInfo.getCode().equals(info.getCode())); |
| if (anyMatch) { |
| current = info; |
| break; |
| } |
| } |
| if (current == null) { |
| |
| checkPermissionInfo.setIsAuth(false); |
| } else { |
| |
| checkPermissionInfo.setIsAuth(true); |
| checkPermissionInfo.setPermissionInfo(current); |
| } |
| return Mono.just(checkPermissionInfo); |
根据以上的判断返回用户信息
| return checkPermissionInfoMono.flatMap(checkPermissionInfo -> { |
| |
| if (checkPermissionInfo.getIsAuth()) { |
| if (checkPermissionInfo.getPermissionInfo() != null) { |
| |
| setCurrentUserInfoAndLog(serverWebExchange, finalUser, checkPermissionInfo.getPermissionInfo()); |
| } |
| ServerHttpRequest build = mutate.build(); |
| return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build()); |
| } else { |
| |
| return getVoidMono(serverWebExchange, new TokenForbiddenResponse("Forbidden!Does not has Permission!"), HttpStatus.FORBIDDEN); |
| } |
| }); |
5.大致流程图

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)