一、项目准备
-
父项目,pom.xml,见微服务项目构建认证中心
-
子项目,pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>hg_blog</artifactId> <groupId>com.jiangming</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>hegu-gateway-service9527</artifactId> <dependencies> <dependency> <groupId>com.jiangming</groupId> <artifactId>hegu-api-commons</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!--gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> </project>
-
项目结构
-
nacos配置文件
spring: cloud: gateway: #跨域设置 globalcors: cors-configurations: '[/**]': allowedOrigins: "*" allowedHeaders: "*" allowCredentials: true allowedMethods: - GET - POST - PUT - DELETE discovery: locator: enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由转发 routes: #id唯一 - id: hegu-auth-jwt #资源 uri: lb://hegu-auth-jwt #断言 predicates: - Path=/login
二、代码
-
application.yml
spring: profiles: active: dev
-
bootstrap.yml
server: port: 9527 spring: application: name: hegu-gateway-service cloud: nacos: discovery: server-addr: nacos地址 #命名空间 namespace: ce58f19d-b36c-4aad-968a-1a1f2280cfc config: server-addr: nacos地址 #命名空间 namespace: ce58f19d-b36c-4aad-968a-1a1f2280cfc file-extension: yaml
-
HeguGatewayServiceMain启动类
package com.jc.springcloud; /** * @program: hg_blog * @description: * @author: hjc * @create: 2022-01-04 19:19 **/ @SpringBootApplication @EnableDiscoveryClient public class HeguGatewayServiceMain { public static void main(String[] args){ SpringApplication.run(HeguGatewayServiceMain.class,args); } }
-
JwtUtil
package com.jc.springcloud.util; /** * @program: hg_blog * @description: token校验类 * @author: hjc * @create: 2022-01-05 14:54 **/ public class JwtUtil { private static final String JWT_PAYLOAD_USER_KEY = "user"; // 公钥,RSA在线生成工具生成私钥对应的公钥 private static final String PUBLIC_KEY = ""; /*** * @description 公钥 * @return java.security.PublicKey * @author hjc * CreateDate 2022/1/5 15:14 */ public static PublicKey getPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { X509EncodedKeySpec spec = new X509EncodedKeySpec(new BASE64Decoder().decodeBuffer(PUBLIC_KEY)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(spec); } public static <T> T getPayLoadFromToken(String token,PublicKey publicKey,Class<T> claType) { Claims claims = Jwts.parser()//获得JWT解析器的编译器对象 .setSigningKey(publicKey)//设置签名(公钥加密签名) .parseClaimsJws(token)//解析token获得JWS对象 .getBody(); return JSON.parseObject(claims.get(JWT_PAYLOAD_USER_KEY).toString(),claType); } }
-
ResourceUtil
package com.jc.springcloud.util; /** * @program: hg_blog * @description: 资源分配类 * @author: hjc * @create: 2022-01-05 16:54 **/ @Slf4j @Component public class ResourceUtil { //公共资源 private static final String[] PUBLIC_PATH = { "/login", }; //用户资源 private static final String[] USER_PATH = { }; //管理员资源 private static final String[] ADMIN_PATH = { }; private static final String AUTH_USER = "ROLE_USER"; private static final String AUTH_ADMIN = "ROLE_ADMIN"; /*** * @description 获取公共资源 * @return java.util.List<java.lang.String> * @author hjc * CreateDate 2022/1/5 17:13 */ public List<String> getPublicPath(){ List<String> path = new ArrayList<>(); Collections.addAll(path,PUBLIC_PATH); return path; } public List<String> getPath(String auth){ List<String> path = new ArrayList<>(); if (AUTH_USER.equals(auth)){ log.info("获取用户权限!"); Collections.addAll(path,USER_PATH); }else if (AUTH_ADMIN.equals(auth)){ log.info("获取管理员权限!"); Collections.addAll(path,ADMIN_PATH); } return path; } /*** * @description 判断资源是否匹配 * @param requestPath 获取路径 * @param path 访问的资源 * @return boolean * @author hjc * CreateDate 2022/1/5 17:49 */ public boolean isResourcePath(String requestPath,List<String> path){ return path.stream() .map(url -> url.replace("/**","")) .anyMatch(requestPath::startsWith); } }
-
AuthorizeFilter
package com.jc.springcloud.filter; /** * @program: hg_blog * @description: 鉴权过滤器 验证token * @author: hjc * @create: 2022-01-05 16:04 **/ @Slf4j @Component public class AuthorizeFilter implements GlobalFilter, Ordered { private static final String AUTHORIZE_TOKEN = "token"; @Resource private ResourceUtil resourceUtil; @SneakyThrows @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //获取请求 ServerHttpRequest request = exchange.getRequest(); //获取响应 ServerHttpResponse response = exchange.getResponse(); //不需要权限的放行 String requestPath = request.getURI().getPath(); if (resourceUtil.isResourcePath(requestPath,resourceUtil.getPublicPath())){ log.info("公共资源->"+requestPath); return chain.filter(exchange); } //获取请求头 HttpHeaders headers = request.getHeaders(); //获取token String token = headers.getFirst(AUTHORIZE_TOKEN); //判断请求头是否有令牌 if (token == null || "".equals(token)){ return unauthorized(response,"请登录!"); } log.info("token->"+token); try{ //解签token,获取payload Map<String,Object> payloadMap = JwtUtil.getPayLoadFromToken(token,JwtUtil.getPublicKey(),Map.class); //获取权限信息 List<Map> auhtMap = (List<Map>) payloadMap.get("auth"); List authList = new ArrayList<>(); for (Map map : auhtMap){ authList.add(map.get("authority")); } int user_id = (int) payloadMap.get("user_id"); String auth = (String) authList.get(0); if (!resourceUtil.isResourcePath(requestPath,resourceUtil.getPath(auth))){ return unauthorized(response,"权限不足!"); } log.info("权限资源->"+requestPath); //将用户user_id放入header中发送给下游业务 request.mutate().header("user_id", String.valueOf(user_id)).build(); log.info(String.valueOf(user_id)); }catch (ExpiredJwtException e){ e.printStackTrace(); return unauthorized(response,"登录过期,请重新登录!"); } return chain.filter(exchange.mutate() .request(request) .build()); } private Mono<Void> unauthorized(ServerHttpResponse response,String message){ response.getHeaders().add(HttpHeaders.CONTENT_TYPE,"application/json;charset=UTF-8"); //401 response.setStatusCode(HttpStatus.UNAUTHORIZED); CommonResult result = CommonResult.fail(HttpStatus.UNAUTHORIZED.value(),message); DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes()); return response.writeWith(Mono.just(dataBuffer)); } @Override public int getOrder() { return 0; } }