mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2024-12-23 05:00:25 +08:00
feat:退出登录添加JWT至黑名单失效
This commit is contained in:
parent
fdf50325c2
commit
da7a012d7d
@ -5,7 +5,6 @@ import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import com.youlai.auth.exception.CustomOAuth2Exception;
|
||||
import com.youlai.common.core.result.ResultCodeEnum;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.youlai.auth.component;
|
||||
|
||||
import com.youlai.auth.exception.CustomOAuth2Exception;
|
||||
import com.youlai.common.core.result.ResultCodeEnum;
|
||||
import com.youlai.common.core.result.ResultCode;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@ -63,12 +63,12 @@ public class OAuth2WebResponseExceptionTranslator implements WebResponseExceptio
|
||||
private static class InvalidException extends OAuth2Exception {
|
||||
|
||||
public InvalidException() {
|
||||
super(ResultCodeEnum.USER_PASSWORD_ERROR.getMsg());
|
||||
super(ResultCode.USERNAME_OR_PASSWORD_ERROR.getMsg());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOAuth2ErrorCode() {
|
||||
return ResultCodeEnum.USER_PASSWORD_ERROR.getCode();
|
||||
return ResultCode.USERNAME_OR_PASSWORD_ERROR.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,40 +1,38 @@
|
||||
package com.youlai.auth.controller;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.nimbusds.jose.JWSObject;
|
||||
import com.youlai.admin.api.dto.UserDTO;
|
||||
import com.youlai.auth.domain.Oauth2Token;
|
||||
import com.youlai.common.core.constant.AuthConstants;
|
||||
import com.youlai.common.core.result.Result;
|
||||
import com.youlai.common.core.result.ResultCode;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import springfox.documentation.annotations.ApiIgnore;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.security.Principal;
|
||||
import java.text.ParseException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Api(tags = "认证中心认证登录")
|
||||
@Api(tags = "认证中心")
|
||||
@RestController
|
||||
@RequestMapping("/oauth")
|
||||
@AllArgsConstructor
|
||||
public class AuthController {
|
||||
|
||||
@Resource
|
||||
private TokenEndpoint tokenEndpoint;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate redisTemplate;
|
||||
|
||||
@ApiOperation("Oauth2获取token")
|
||||
@ -61,14 +59,19 @@ public class AuthController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/logout")
|
||||
public Result logout(HttpServletRequest request) throws ParseException {
|
||||
String token = request.getHeader(AuthConstants.JWT_TOKEN_HEADER);
|
||||
JWSObject jwsObject = JWSObject.parse(token);
|
||||
String payload = jwsObject.getPayload().toString(); // jwt 载体部分
|
||||
UserDTO userDTO = JSONUtil.toBean(payload, UserDTO.class);
|
||||
redisTemplate.opsForValue().set("", "");
|
||||
return null;
|
||||
public Result logout(HttpServletRequest request) {
|
||||
String payload = request.getHeader(AuthConstants.JWT_PAYLOAD_KEY);
|
||||
JSONObject jsonObject = JSONUtil.parseObj(payload);
|
||||
|
||||
String jti = jsonObject.getStr("jti"); // JWT唯一标识
|
||||
long exp = jsonObject.getLong("exp"); // JWT过期时间戳
|
||||
|
||||
long currentTimeSeconds = System.currentTimeMillis() / 1000;
|
||||
|
||||
if (exp < currentTimeSeconds) { // token已过期
|
||||
return Result.custom(ResultCode.INVALID_TOKEN_OR_EXPIRED);
|
||||
}
|
||||
redisTemplate.opsForValue().set(AuthConstants.TOKEN_BLACKLIST_PREFIX + jti, null, (exp - currentTimeSeconds), TimeUnit.SECONDS);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@ package com.youlai.gateway.component;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.youlai.common.core.result.Result;
|
||||
import com.youlai.common.core.result.ResultCodeEnum;
|
||||
import com.youlai.common.core.result.ResultCode;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@ -29,7 +29,7 @@ public class CustomServerAccessDeniedHandler implements ServerAccessDeniedHandle
|
||||
response.getHeaders().set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
response.getHeaders().set("Access-Control-Allow-Origin","*");
|
||||
response.getHeaders().set("Cache-Control","no-cache");
|
||||
String body= JSONUtil.toJsonStr(Result.custom(ResultCodeEnum.USER_ACCESS_UNAUTHORIZED));
|
||||
String body= JSONUtil.toJsonStr(Result.custom(ResultCode.USER_ACCESS_UNAUTHORIZED));
|
||||
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
|
||||
return response.writeWith(Mono.just(buffer));
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package com.youlai.gateway.component;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.youlai.common.core.result.Result;
|
||||
import com.youlai.common.core.result.ResultCodeEnum;
|
||||
import com.youlai.common.core.result.ResultCode;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@ -29,7 +29,7 @@ public class CustomServerAuthenticationEntryPoint implements ServerAuthenticatio
|
||||
response.getHeaders().set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
response.getHeaders().set("Access-Control-Allow-Origin", "*");
|
||||
response.getHeaders().set("Cache-Control", "no-cache");
|
||||
String body = JSONUtil.toJsonStr(Result.custom(ResultCodeEnum.USER_ACCOUNT_UNAUTHENTICATED));
|
||||
String body = JSONUtil.toJsonStr(Result.custom(ResultCode.USER_ACCOUNT_UNAUTHENTICATED));
|
||||
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
|
||||
return response.writeWith(Mono.just(buffer));
|
||||
}
|
||||
|
@ -6,14 +6,12 @@ import com.youlai.common.core.constant.AuthConstants;
|
||||
import com.youlai.gateway.component.AuthorizationManager;
|
||||
import com.youlai.gateway.component.CustomServerAuthenticationEntryPoint;
|
||||
import com.youlai.gateway.component.CustomServerAccessDeniedHandler;
|
||||
import com.youlai.gateway.filter.WhiteListRemoveJwtFilter;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||
|
@ -1,26 +1,42 @@
|
||||
package com.youlai.gateway.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.nimbusds.jose.JWSObject;
|
||||
import com.youlai.common.core.constant.AuthConstants;
|
||||
import com.youlai.common.core.result.Result;
|
||||
import com.youlai.common.core.result.ResultCode;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.data.redis.core.RedisTemplate;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* JWT转换成用户信息过滤器
|
||||
* 黑名单token过滤器
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class AuthGlobalFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate redisTemplate;
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
@ -31,9 +47,25 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
|
||||
token = token.replace(AuthConstants.JWT_TOKEN_PREFIX, Strings.EMPTY);
|
||||
JWSObject jwsObject = JWSObject.parse(token);
|
||||
String payload = jwsObject.getPayload().toString();
|
||||
|
||||
// 黑名单token(登出、修改密码)校验
|
||||
JSONObject jsonObject = JSONUtil.parseObj(payload);
|
||||
String jti = jsonObject.getStr("jti"); // JWT唯一标识
|
||||
|
||||
Boolean isBlack = redisTemplate.hasKey(AuthConstants.TOKEN_BLACKLIST_PREFIX + jti);
|
||||
if (isBlack) {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.setStatusCode(HttpStatus.OK);
|
||||
response.getHeaders().set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
response.getHeaders().set("Access-Control-Allow-Origin", "*");
|
||||
response.getHeaders().set("Cache-Control", "no-cache");
|
||||
String body = JSONUtil.toJsonStr(Result.custom(ResultCode.INVALID_TOKEN_OR_EXPIRED));
|
||||
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
|
||||
return response.writeWith(Mono.just(buffer));
|
||||
}
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.header(AuthConstants.USER_TOKEN_HEADER, payload)
|
||||
.header(AuthConstants.JWT_TOKEN_HEADER,token)
|
||||
.header(AuthConstants.JWT_PAYLOAD_KEY, payload)
|
||||
.build();
|
||||
exchange = exchange.mutate().request(request).build();
|
||||
return chain.filter(exchange);
|
||||
|
@ -1,47 +0,0 @@
|
||||
package com.youlai.gateway.filter;
|
||||
|
||||
import com.youlai.common.core.constant.AuthConstants;
|
||||
import com.youlai.gateway.config.WhiteListConfig;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 白名单路径移除JWT请求头
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class WhiteListRemoveJwtFilter implements WebFilter {
|
||||
|
||||
private WhiteListConfig whiteListConfig;
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
URI uri = request.getURI();
|
||||
PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
//白名单路径移除JWT请求头
|
||||
List<String> ignoreUrls = whiteListConfig.getUrls();
|
||||
for (String ignoreUrl : ignoreUrls) {
|
||||
if (pathMatcher.match(ignoreUrl, uri.getPath())) {
|
||||
request = exchange.getRequest().mutate()
|
||||
.header(AuthConstants.JWT_TOKEN_HEADER, Strings.EMPTY).build();
|
||||
exchange = exchange.mutate().request(request).build();
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user