网关整合token校验
1.先引入依赖
<!--JJWT依赖--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>javax.activation</groupId> <artifactId>activation</artifactId> <version>1.1.1</version> </dependency>
2.做一个过滤器来拦截请求
在做这个过滤器前,引入一个需要的工具类
package com.itentbase.job.gateway.utils; import com.alibaba.nacos.common.model.RestResult; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.itentbase.job.common.enums.ResponseEnum; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpResponse; import reactor.core.publisher.Mono; /** *在响应里面添加自定义json数据 * * @author 陈光铭 * @author:陈光铭 * @create: 2023-10-30 15:03 * @Description: xxxx * @date 2023/10/30--03:03:57 */ public class ResponseUtils { /** * 这个是token验证失败的固定返回响应,里面的返回响应状态码和提示消息都可以自定义 * * @param response 回答 * @return {@link Mono }<{@link Void }> * @author 陈光铭 * @time 2023/10/30--03:24:19 **/ public static Mono<Void> setResponse(ServerHttpResponse response){ response.setStatusCode(HttpStatus.UNAUTHORIZED); DataBufferFactory bufferFactory = response.bufferFactory(); ObjectMapper objectMapper = new ObjectMapper(); Mono<Void> mono=null; try { DataBuffer wrap = bufferFactory.wrap(objectMapper.writeValueAsBytes(new RestResult<>(ResponseEnum.STATUS_HAS_EXPIRED.getCode(),ResponseEnum.STATUS_HAS_EXPIRED.getMessage()))); mono = response.writeWith(Mono.fromSupplier(() -> wrap)); } catch (JsonProcessingException e) { e.printStackTrace(); } return mono; } }
package com.itentbase.job.gateway.filter; import com.alibaba.nacos.common.model.RestResult; import com.fasterxml.jackson.databind.ObjectMapper; import com.itentbase.job.common.enums.ResponseEnum; import com.itentbase.job.common.utils.JwtUtils; import com.itentbase.job.gateway.utils.ResponseUtils; import lombok.SneakyThrows; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * 授权筛选器 * 鉴权过滤器 验证token * * @author 陈光铭 * @date 2023/10/30--02:22:18 */ @Component public class AuthorizeFilter implements GlobalFilter, Ordered { private static final String AUTHORIZE_TOKEN = "token"; @SneakyThrows @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //1. 获取请求 ServerHttpRequest request = exchange.getRequest(); //2. 则获取响应 ServerHttpResponse response = exchange.getResponse(); //3. 如果是登录请求则放行 if (request.getURI().getPath().contains("/user/user/doLogin")||request.getURI().getPath().contains("/sendMessage/message/sendCode")) { return chain.filter(exchange); } //4. 获取请求头 HttpHeaders headers = request.getHeaders(); //5. 请求头中获取令牌 String token = headers.getFirst(AUTHORIZE_TOKEN); //6. 判断请求头中是否有令牌 if (StringUtils.isEmpty(token)) { //7. 返回 return ResponseUtils.setResponse(response); } //9. 如果请求头中有令牌则解析令牌 try { JwtUtils.decrypt(token); } catch (Exception e) { e.printStackTrace(); //10. 解析jwt令牌出错, 说明令牌过期或者伪造等不合法情况出现 //11. 返回 return ResponseUtils.setResponse(response); } //12. 放行 return chain.filter(exchange); } @Override public int getOrder() { return 0; } }
3.在controller里面拿到请求和需要的数据
先引入一个jwt的工具类,后面会用到
package com.itentbase.job.common.utils; import com.itentbase.job.common.enums.TimeEnum; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; /** * JWT Utils * * @author 陈光铭 * @author:陈光铭 * @create: 2023-10-30 11:02 * @Description: xxxx * @date 2023/10/30--11:43:44 */ public class JwtUtils { /** * 生成token * * @param account 账户 * @return {@link String } * @author 陈光铭 * @time 2023/10/30--11:33:48 **/ public static String encipher(String account){ //下面的Constant.TOKEN_KEY任意你想要的字符串都可以 JwtBuilder builder= Jwts.builder() .setAudience(account) .setExpiration(new Date(System.currentTimeMillis()+ TimeEnum.ONE_MONTH.getTime())) .signWith(SignatureAlgorithm.HS256,Constant.TOKEN_KEY); //构建 并返回一个字符串 return builder.compact(); } /** * 解密获取用户的账号 * * @param token 代币 * @return {@link String } * @author 陈光铭 * @time 2023/10/30--11:35:30 **/ public static String decrypt(String token){ Claims claim = Jwts.parser().setSigningKey(Constant.TOKEN_KEY).parseClaimsJws(token).getBody(); return claim.getAudience(); } /** * 判断token是否过期 * * @param token 代币 * @return boolean * @author 陈光铭 * @time 2023/10/30--11:38:44 **/ public static boolean isExpiration(String token){ Claims claim = Jwts.parser().setSigningKey(Constant.TOKEN_KEY).parseClaimsJws(token).getBody(); return claim.getExpiration().getTime() - System.currentTimeMillis() > 0; } }
@PostMapping("/doLogin") //userRegisterVo是你需要的数据,可以是账号和密码任何你想要的数据 public R doLogin(String phone,String code){ //这里做你需要的校验,这里随便做一下 if(phone==code){ //JwtUtils.encipher(user.getAccount())就是根据账号来生成一个token,这个账号最好不要放敏感信息 return s.successLogin(JwtUtils.encipher(user.getAccount())); } //验证失败,返回错误 return Rs.error();
这样前台就可以在doLogin的响应里面拿到token,在后面的请求中就可以校验token