mirror of https://gitee.com/maxjhandsome/pig
🐛 Fixing a bug. refresh-token error
This commit is contained in:
parent
d1367808e5
commit
935a89ab42
|
@ -25,6 +25,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
|
||||
|
@ -49,7 +50,7 @@ import java.util.Map;
|
|||
@EnableAuthorizationServer
|
||||
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
|
||||
private final DataSource dataSource;
|
||||
private final org.springframework.security.core.userdetails.UserDetailsService UserDetailsService;
|
||||
private final UserDetailsService userDetailsService;
|
||||
private final AuthenticationManager authenticationManager;
|
||||
private final RedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
|
@ -74,7 +75,7 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
|
|||
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
|
||||
.tokenStore(tokenStore())
|
||||
.tokenEnhancer(tokenEnhancer())
|
||||
.userDetailsService(UserDetailsService)
|
||||
.userDetailsService(userDetailsService)
|
||||
.authenticationManager(authenticationManager)
|
||||
.reuseRefreshTokens(false)
|
||||
.exceptionTranslator(new PigWebResponseExceptionTranslator());
|
||||
|
|
|
@ -69,4 +69,8 @@ public interface CommonConstants {
|
|||
*/
|
||||
Integer FAIL = 1;
|
||||
|
||||
/**
|
||||
* 验证码前缀
|
||||
*/
|
||||
String DEFAULT_CODE_KEY = "DEFAULT_CODE_KEY_";
|
||||
}
|
||||
|
|
|
@ -16,10 +16,15 @@
|
|||
|
||||
package com.pig4cloud.pig.common.core.util;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.pig4cloud.pig.common.core.exception.CheckedException;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
@ -31,6 +36,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -40,8 +46,8 @@ import java.io.PrintWriter;
|
|||
*/
|
||||
@Slf4j
|
||||
public class WebUtils extends org.springframework.web.util.WebUtils {
|
||||
|
||||
public static final String UNKNOWN = "unknown";
|
||||
private static final String BASIC_ = "Basic ";
|
||||
private static final String UNKNOWN = "unknown";
|
||||
|
||||
/**
|
||||
* 判断是否ajax请求
|
||||
|
@ -188,5 +194,36 @@ public class WebUtils extends org.springframework.web.util.WebUtils {
|
|||
}
|
||||
return StringUtils.isBlank(ip) ? null : ip.split(",")[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从request 获取CLIENT_ID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static String[] getClientId(ServerHttpRequest request) {
|
||||
String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
|
||||
if (header == null || !header.startsWith(BASIC_)) {
|
||||
throw new CheckedException("请求头中client信息为空");
|
||||
}
|
||||
byte[] base64Token = header.substring(6).getBytes("UTF-8");
|
||||
byte[] decoded;
|
||||
try {
|
||||
decoded = Base64.decode(base64Token);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new CheckedException(
|
||||
"Failed to decode basic authentication token");
|
||||
}
|
||||
|
||||
String token = new String(decoded, StandardCharsets.UTF_8);
|
||||
|
||||
int delim = token.indexOf(":");
|
||||
|
||||
if (delim == -1) {
|
||||
throw new CheckedException("Invalid basic authentication token");
|
||||
}
|
||||
return new String[]{token.substring(0, delim), token.substring(delim + 1)};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ spring:
|
|||
- Path=/auth/**
|
||||
filters:
|
||||
# 验证码处理
|
||||
- ImageCodeGatewayFilter
|
||||
- ValidateCodeGatewayFilter
|
||||
# 前端密码解密
|
||||
- PasswordDecoderFilter
|
||||
#UPMS 模块
|
||||
|
@ -42,3 +42,8 @@ security:
|
|||
encode:
|
||||
# 前端密码密钥,必须16位
|
||||
key: 'thanks,pig4cloud'
|
||||
|
||||
# 不校验验证码终端
|
||||
ignore:
|
||||
clients:
|
||||
- test
|
||||
|
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020, 冷冷 (wangiegie@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the GNU Lesser General Public License 3.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* https://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pig.gateway.filter;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pig.common.core.exception.CheckedException;
|
||||
import com.pig4cloud.pig.common.core.exception.ValidateCodeException;
|
||||
import com.pig4cloud.pig.common.core.config.FilterIgnorePropertiesConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
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 java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019/2/1
|
||||
* 验证码处理
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ImageCodeGatewayFilter extends AbstractGatewayFilterFactory {
|
||||
public static final String DEFAULT_CODE_KEY = "DEFAULT_CODE_KEY";
|
||||
public static final String OAUTH_TOKEN_URL = "/oauth/token";
|
||||
private static final String BASIC_ = "Basic ";
|
||||
@Autowired
|
||||
private RedisTemplate redisTemplate;
|
||||
@Autowired
|
||||
private FilterIgnorePropertiesConfig filterIgnorePropertiesConfig;
|
||||
|
||||
/**
|
||||
* 从header 请求中的clientId/clientsecect
|
||||
*
|
||||
* @param header header中的参数
|
||||
* @throws CheckedException if the Basic header is not present or is not valid
|
||||
* Base64
|
||||
*/
|
||||
public static String[] extractAndDecodeHeader(String header)
|
||||
throws IOException, CheckedException {
|
||||
|
||||
byte[] base64Token = header.substring(6).getBytes("UTF-8");
|
||||
byte[] decoded;
|
||||
try {
|
||||
decoded = Base64.decode(base64Token);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new CheckedException(
|
||||
"Failed to decode basic authentication token");
|
||||
}
|
||||
|
||||
String token = new String(decoded, CharsetUtil.UTF_8);
|
||||
|
||||
int delim = token.indexOf(":");
|
||||
|
||||
if (delim == -1) {
|
||||
throw new CheckedException("Invalid basic authentication token");
|
||||
}
|
||||
return new String[]{token.substring(0, delim), token.substring(delim + 1)};
|
||||
}
|
||||
|
||||
/**
|
||||
* *从header 请求中的clientId/clientsecect
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String[] extractAndDecodeHeader(ServerHttpRequest request)
|
||||
throws IOException, CheckedException {
|
||||
String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
|
||||
if (header == null || !header.startsWith(BASIC_)) {
|
||||
throw new CheckedException("请求头中client信息为空");
|
||||
}
|
||||
|
||||
return extractAndDecodeHeader(header);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Object config) {
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
|
||||
// 不是登录请求,直接向下执行
|
||||
if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), OAUTH_TOKEN_URL)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// 终端设置不校验, 直接向下执行(1. 从请求参数中获取 2.从header取)
|
||||
String clientId = request.getQueryParams().getFirst("client_id");
|
||||
if (StrUtil.isNotBlank(clientId)) {
|
||||
if (filterIgnorePropertiesConfig.getClients().contains(clientId)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
}
|
||||
try {
|
||||
String[] clientInfos = extractAndDecodeHeader(request);
|
||||
if (filterIgnorePropertiesConfig.getClients().contains(clientInfos[0])) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED);
|
||||
return response.setComplete();
|
||||
}
|
||||
|
||||
//校验验证码合法性
|
||||
try {
|
||||
checkCode(request);
|
||||
} catch (ValidateCodeException e) {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED);
|
||||
return response.setComplete();
|
||||
}
|
||||
|
||||
return chain.filter(exchange);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查code
|
||||
*
|
||||
* @param request
|
||||
* @throws ValidateCodeException 校验异常
|
||||
*/
|
||||
private void checkCode(ServerHttpRequest request) throws ValidateCodeException {
|
||||
String code = request.getQueryParams().getFirst("code");
|
||||
|
||||
if (StrUtil.isBlank(code)) {
|
||||
throw new ValidateCodeException();
|
||||
}
|
||||
|
||||
String randomStr = request.getQueryParams().getFirst("randomStr");
|
||||
if (StrUtil.isBlank(randomStr)) {
|
||||
throw new ValidateCodeException();
|
||||
}
|
||||
|
||||
String key = DEFAULT_CODE_KEY + randomStr;
|
||||
if (!redisTemplate.hasKey(key)) {
|
||||
throw new ValidateCodeException();
|
||||
}
|
||||
|
||||
Object codeObj = redisTemplate.opsForValue().get(key);
|
||||
|
||||
if (codeObj == null) {
|
||||
throw new ValidateCodeException();
|
||||
}
|
||||
|
||||
String saveCode = codeObj.toString();
|
||||
if (StrUtil.isBlank(saveCode)) {
|
||||
redisTemplate.delete(key);
|
||||
throw new ValidateCodeException();
|
||||
}
|
||||
|
||||
if (!StrUtil.equals(saveCode, code)) {
|
||||
redisTemplate.delete(key);
|
||||
throw new ValidateCodeException();
|
||||
}
|
||||
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020, 冷冷 (wangiegie@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the GNU Lesser General Public License 3.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* https://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pig.gateway.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pig4cloud.pig.common.core.config.FilterIgnorePropertiesConfig;
|
||||
import com.pig4cloud.pig.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.core.exception.ValidateCodeException;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import com.pig4cloud.pig.common.core.util.WebUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
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 reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/7/4
|
||||
* 验证码处理
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory {
|
||||
private final ObjectMapper objectMapper;
|
||||
private final RedisTemplate redisTemplate;
|
||||
private final FilterIgnorePropertiesConfig filterIgnorePropertiesConfig;
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Object config) {
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
|
||||
// 不是登录请求,直接向下执行
|
||||
if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath()
|
||||
, SecurityConstants.OAUTH_TOKEN_URL)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// 刷新token,直接向下执行
|
||||
String grantType = request.getQueryParams().getFirst("grant_type");
|
||||
if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// 终端设置不校验, 直接向下执行
|
||||
try {
|
||||
String[] clientInfos = WebUtils.getClientId(request);
|
||||
if (filterIgnorePropertiesConfig.getClients().contains(clientInfos[0])) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
//校验验证码
|
||||
checkCode(request);
|
||||
} catch (Exception e) {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED);
|
||||
try {
|
||||
return response.writeWith(Mono.just(response.bufferFactory()
|
||||
.wrap(objectMapper.writeValueAsBytes(
|
||||
R.builder().msg(e.getMessage())
|
||||
.code(CommonConstants.FAIL).build()))));
|
||||
} catch (JsonProcessingException e1) {
|
||||
log.error("对象输出异常", e1);
|
||||
}
|
||||
}
|
||||
|
||||
return chain.filter(exchange);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查code
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
@SneakyThrows
|
||||
private void checkCode(ServerHttpRequest request) {
|
||||
String code = request.getQueryParams().getFirst("code");
|
||||
|
||||
if (StrUtil.isBlank(code)) {
|
||||
throw new ValidateCodeException("验证码不能为空");
|
||||
}
|
||||
|
||||
String randomStr = request.getQueryParams().getFirst("randomStr");
|
||||
if (StrUtil.isBlank(randomStr)) {
|
||||
randomStr = request.getQueryParams().getFirst("mobile");
|
||||
}
|
||||
|
||||
String key = CommonConstants.DEFAULT_CODE_KEY + randomStr;
|
||||
if (!redisTemplate.hasKey(key)) {
|
||||
throw new ValidateCodeException("验证码不合法");
|
||||
}
|
||||
|
||||
Object codeObj = redisTemplate.opsForValue().get(key);
|
||||
|
||||
if (codeObj == null) {
|
||||
throw new ValidateCodeException("验证码不合法");
|
||||
}
|
||||
|
||||
String saveCode = codeObj.toString();
|
||||
if (StrUtil.isBlank(saveCode)) {
|
||||
redisTemplate.delete(key);
|
||||
throw new ValidateCodeException("验证码不合法");
|
||||
}
|
||||
|
||||
if (!StrUtil.equals(saveCode, code)) {
|
||||
redisTemplate.delete(key);
|
||||
throw new ValidateCodeException("验证码不合法");
|
||||
}
|
||||
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
package com.pig4cloud.pig.gateway.handler;
|
||||
|
||||
import com.google.code.kaptcha.Producer;
|
||||
import com.pig4cloud.pig.gateway.filter.ImageCodeGatewayFilter;
|
||||
import com.pig4cloud.pig.common.core.constant.CommonConstants;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
|
@ -57,7 +57,7 @@ public class ImageCodeHandler implements HandlerFunction<ServerResponse> {
|
|||
|
||||
//保存验证码信息
|
||||
String randomStr = serverRequest.queryParam("randomStr").get();
|
||||
redisTemplate.opsForValue().set(ImageCodeGatewayFilter.DEFAULT_CODE_KEY + randomStr, text, 60, TimeUnit.SECONDS);
|
||||
redisTemplate.opsForValue().set(CommonConstants.DEFAULT_CODE_KEY + randomStr, text, 60, TimeUnit.SECONDS);
|
||||
|
||||
// 转换流信息写出
|
||||
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
|
||||
|
|
Loading…
Reference in New Issue