mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2024-12-23 05:00:25 +08:00
Merge branch 'master' into dev
This commit is contained in:
commit
efd0fa482a
@ -37,7 +37,7 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>common-database</artifactId>
|
||||
<artifactId>common-mybatis</artifactId>
|
||||
<version>${youlai.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>common-database</artifactId>
|
||||
<artifactId>common-mybatis</artifactId>
|
||||
<version>${youlai.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
@ -1,14 +1,21 @@
|
||||
package com.youlai.auth.config;
|
||||
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.youlai.auth.domain.User;
|
||||
import com.youlai.auth.filter.CustomClientCredentialsTokenEndpointFilter;
|
||||
import com.youlai.auth.service.JdbcClientDetailsServiceImpl;
|
||||
import com.youlai.auth.service.UserDetailsServiceImpl;
|
||||
import com.youlai.common.core.constant.AuthConstants;
|
||||
import com.youlai.common.core.result.Result;
|
||||
import com.youlai.common.core.result.ResultCode;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
|
||||
@ -20,6 +27,7 @@ import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.security.KeyPair;
|
||||
@ -63,22 +71,48 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
|
||||
tokenEnhancers.add(jwtAccessTokenConverter());
|
||||
tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
|
||||
|
||||
endpoints.authenticationManager(authenticationManager)
|
||||
endpoints
|
||||
|
||||
.authenticationManager(authenticationManager)
|
||||
.accessTokenConverter(jwtAccessTokenConverter())
|
||||
.tokenEnhancer(tokenEnhancerChain)
|
||||
.userDetailsService(userDetailsService)
|
||||
// refresh token有两种使用方式:重复使用(true)、非重复使用(false),默认为true
|
||||
// 1 重复使用:access token过期刷新时, refresh token过期时间未改变,仍以初次生成的时间为准
|
||||
// 2 非重复使用:access token过期刷新时, refresh token过期时间延续,在refresh token有效期内刷新便永不失效达到无需再次登录的目的
|
||||
.reuseRefreshTokens(false);
|
||||
.reuseRefreshTokens(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 允许表单认证
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void configure(AuthorizationServerSecurityConfigurer security) {
|
||||
security.allowFormAuthenticationForClients();
|
||||
/*security.allowFormAuthenticationForClients();*/
|
||||
CustomClientCredentialsTokenEndpointFilter endpointFilter = new CustomClientCredentialsTokenEndpointFilter(security);
|
||||
endpointFilter.afterPropertiesSet();
|
||||
endpointFilter.setAuthenticationEntryPoint(authenticationEntryPoint());
|
||||
security.addTokenEndpointAuthenticationFilter(endpointFilter);
|
||||
|
||||
security.authenticationEntryPoint(authenticationEntryPoint())
|
||||
.tokenKeyAccess("isAuthenticated()")
|
||||
.checkTokenAccess("permitAll()");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自定义认证异常响应数据
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationEntryPoint authenticationEntryPoint() {
|
||||
return (request, response, e) -> {
|
||||
response.setStatus(HttpStatus.HTTP_OK);
|
||||
response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
Result result = Result.custom(ResultCode.CLIENT_AUTHENTICATION_FAILED);
|
||||
response.getWriter().print(JSONUtil.toJsonStr(result));
|
||||
response.getWriter().flush();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.youlai.auth.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -12,20 +13,23 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@Slf4j
|
||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
|
||||
.and()
|
||||
.authorizeRequests().antMatchers("/rsa/publicKey","/oauth/logout").permitAll().anyRequest().authenticated()
|
||||
.and()
|
||||
.csrf().disable();
|
||||
// 登录失败处理handler,返回一段json
|
||||
http
|
||||
.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
|
||||
.and()
|
||||
.authorizeRequests().antMatchers("/oauth/public_key", "/oauth/logout").permitAll().anyRequest().authenticated()
|
||||
.and()
|
||||
.csrf().disable();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果不配置SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户
|
||||
* 如果不配置SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
@ -33,7 +37,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,20 +4,24 @@ import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
|
||||
import cn.binarywang.wx.miniapp.config.WxMaConfig;
|
||||
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
public class WxConfig {
|
||||
private WxProperties properties;
|
||||
|
||||
@Value("${wx.miniapp.appid}")
|
||||
private String appid;
|
||||
|
||||
@Value("${wx.miniapp.secret}")
|
||||
private String secret;
|
||||
|
||||
@Bean
|
||||
public WxMaConfig wxMaConfig() {
|
||||
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
|
||||
config.setAppid(properties.getAppid());
|
||||
config.setSecret(properties.getSecret());
|
||||
config.setAppid(appid);
|
||||
config.setSecret(secret);
|
||||
return config;
|
||||
}
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
package com.youlai.auth.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "wx.miniapp")
|
||||
public class WxProperties {
|
||||
|
||||
/**
|
||||
* 设置微信小程序的appid
|
||||
*/
|
||||
private String appid;
|
||||
|
||||
/**
|
||||
* 设置微信小程序的Secret
|
||||
*/
|
||||
private String secret;
|
||||
}
|
@ -6,6 +6,8 @@ import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import com.youlai.auth.domain.Oauth2Token;
|
||||
import com.youlai.common.core.constant.AuthConstants;
|
||||
import com.youlai.common.core.constant.Constants;
|
||||
@ -20,6 +22,7 @@ import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
@ -31,7 +34,9 @@ import org.springframework.web.bind.annotation.*;
|
||||
import springfox.documentation.annotations.ApiIgnore;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.security.KeyPair;
|
||||
import java.security.Principal;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -39,6 +44,7 @@ import java.util.concurrent.TimeUnit;
|
||||
@RestController
|
||||
@RequestMapping("/oauth")
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class AuthController {
|
||||
|
||||
private TokenEndpoint tokenEndpoint;
|
||||
@ -46,6 +52,7 @@ public class AuthController {
|
||||
private WxMaService wxService;
|
||||
private UmsMemberFeignClient umsMemberFeignClient;
|
||||
private PasswordEncoder passwordEncoder;
|
||||
private KeyPair keyPair;
|
||||
|
||||
|
||||
@ApiOperation("OAuth2认证生成token")
|
||||
@ -66,7 +73,6 @@ public class AuthController {
|
||||
@ApiIgnore Principal principal,
|
||||
@ApiIgnore @RequestParam Map<String, String> parameters
|
||||
) throws HttpRequestMethodNotSupportedException, WxErrorException {
|
||||
|
||||
String clientId = parameters.get("client_id");
|
||||
|
||||
if (StrUtil.isBlank(clientId)) {
|
||||
@ -74,8 +80,8 @@ public class AuthController {
|
||||
}
|
||||
|
||||
// 微信小程序端认证处理
|
||||
if(AuthConstants.WEAPP_CLIENT_ID.equals(clientId)){
|
||||
return this.handleForWxAppAuth(principal,parameters);
|
||||
if (AuthConstants.WEAPP_CLIENT_ID.equals(clientId)) {
|
||||
return this.handleForWxAppAuth(principal, parameters);
|
||||
}
|
||||
|
||||
OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
|
||||
@ -162,4 +168,11 @@ public class AuthController {
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/public_key")
|
||||
public Map<String, Object> getKey() {
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
RSAKey key = new RSAKey.Builder(publicKey).build();
|
||||
return new JWKSet(key).toJSONObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
package com.youlai.auth.controller;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* RSA公钥开放接口
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
public class PublicKeyController {
|
||||
|
||||
private KeyPair keyPair;
|
||||
|
||||
@GetMapping("/rsa/publicKey")
|
||||
public Map<String, Object> getKey() {
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
RSAKey key = new RSAKey.Builder(publicKey).build();
|
||||
return new JWKSet(key).toJSONObject();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.youlai.auth.exception;
|
||||
|
||||
import com.youlai.common.core.result.Result;
|
||||
import com.youlai.common.core.result.ResultCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class AuthExceptionHandler {
|
||||
|
||||
/**
|
||||
* 用户名和密码异常
|
||||
*
|
||||
* @param e
|
||||
* @return
|
||||
*/
|
||||
@ExceptionHandler(InvalidGrantException.class)
|
||||
public Result handleInvalidGrantException(InvalidGrantException e) {
|
||||
return Result.custom(ResultCode.USERNAME_OR_PASSWORD_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 账户异常(禁用、锁定、过期)
|
||||
*
|
||||
* @param e
|
||||
* @return
|
||||
*/
|
||||
@ExceptionHandler({InternalAuthenticationServiceException.class})
|
||||
public Result handleInternalAuthenticationServiceException(InternalAuthenticationServiceException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.youlai.auth.filter;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
|
||||
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
|
||||
/**
|
||||
* 重写filter实现客户端自定义异常处理
|
||||
*/
|
||||
public class CustomClientCredentialsTokenEndpointFilter extends ClientCredentialsTokenEndpointFilter {
|
||||
|
||||
private AuthorizationServerSecurityConfigurer configurer;
|
||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
|
||||
public CustomClientCredentialsTokenEndpointFilter(AuthorizationServerSecurityConfigurer configurer) {
|
||||
this.configurer = configurer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
|
||||
super.setAuthenticationEntryPoint(null);
|
||||
this.authenticationEntryPoint = authenticationEntryPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthenticationManager getAuthenticationManager() {
|
||||
return configurer.and().getSharedObject(AuthenticationManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
setAuthenticationFailureHandler((request, response, e) -> authenticationEntryPoint.commence(request, response, e));
|
||||
setAuthenticationSuccessHandler((request, response, authentication) -> {
|
||||
});
|
||||
}
|
||||
}
|
@ -39,7 +39,9 @@ public class UserDetailsServiceImpl implements UserDetailsService {
|
||||
switch (clientId) {
|
||||
case AuthConstants.ADMIN_CLIENT_ID: // 后台用户
|
||||
Result<UserDTO> userResult = adminUserFeignClient.loadUserByUsername(username);
|
||||
if (userResult == null || !ResultCode.SUCCESS.getCode().equals(userResult.getCode())) {
|
||||
if (userResult == null || !ResultCode.SUCCESS.getCode().equals(userResult.getCode())
|
||||
|| userResult.getData() == null
|
||||
) {
|
||||
throw new UsernameNotFoundException("用户不存在");
|
||||
}
|
||||
UserDTO userDTO = userResult.getData();
|
||||
@ -48,7 +50,8 @@ public class UserDetailsServiceImpl implements UserDetailsService {
|
||||
break;
|
||||
case AuthConstants.WEAPP_CLIENT_ID: // 小程序会员
|
||||
Result<MemberDTO> memberResult = umsMemberFeignClient.loadMemberByOpenid(username);
|
||||
if (memberResult == null || !ResultCode.SUCCESS.getCode().equals(memberResult.getCode())) {
|
||||
if (memberResult == null || !ResultCode.SUCCESS.getCode().equals(memberResult.getCode())
|
||||
||memberResult.getData()==null) {
|
||||
throw new UsernameNotFoundException("会员不存在");
|
||||
}
|
||||
MemberDTO memberDTO = memberResult.getData();
|
||||
@ -62,12 +65,8 @@ public class UserDetailsServiceImpl implements UserDetailsService {
|
||||
throw new LockedException("该账号已被锁定!");
|
||||
} else if (!user.isAccountNonExpired()) {
|
||||
throw new AccountExpiredException("该账号已过期!");
|
||||
} else if (!user.isCredentialsNonExpired()) {
|
||||
throw new CredentialsExpiredException("该账户的登录凭证已过期,请重新登录!");
|
||||
}
|
||||
return user;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ public interface AuthConstants {
|
||||
/**
|
||||
* 有来商城后台管理客户端ID
|
||||
*/
|
||||
String ADMIN_CLIENT_ID="youlai-mall-admin";
|
||||
String ADMIN_CLIENT_ID="youlai-admin";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -24,6 +24,7 @@ public enum ResultCode implements IResultCode, Serializable {
|
||||
|
||||
USERNAME_OR_PASSWORD_ERROR("A0210","用户名或密码错误"),
|
||||
INPUT_PASSWORD_EXCEED_LIMIT("A0211","用户输入密码次数超限"),
|
||||
CLIENT_AUTHENTICATION_FAILED("A0212","客户端认证失败"), // *
|
||||
TOKEN_INVALID_OR_EXPIRED("A0230","token无效或已过期"),
|
||||
|
||||
USER_AUTHORIZED_ERROR ("A0300","访问权限异常"),
|
||||
|
@ -9,7 +9,7 @@
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common-database</artifactId>
|
||||
<artifactId>common-mybatis</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@ -27,4 +27,4 @@
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
@ -1,8 +1,8 @@
|
||||
package com.youlai.common.database.config;
|
||||
package com.youlai.common.mybatis.config;
|
||||
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import com.youlai.common.database.handler.MetaHandler;
|
||||
import com.youlai.common.mybatis.handler.MetaHandler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
@ -29,4 +29,5 @@ public class MybatisPlusConfig {
|
||||
globalConfig.setMetaObjectHandler(new MetaHandler());
|
||||
return globalConfig;
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.youlai.common.database.handler;
|
||||
package com.youlai.common.mybatis.handler;
|
||||
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
@ -6,9 +6,6 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author haoxr
|
||||
**/
|
||||
@Component
|
||||
public class MetaHandler implements MetaObjectHandler {
|
||||
|
||||
@ -22,4 +19,5 @@ public class MetaHandler implements MetaObjectHandler {
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
this.setFieldValByName("gmtCreate", new Date(), metaObject);
|
||||
}
|
||||
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.youlai.common.database.config.MybatisPlusConfig
|
||||
com.youlai.common.mybatis.config.MybatisPlusConfig
|
@ -22,10 +22,10 @@
|
||||
<version>${youlai.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- spring boot web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
<module>common-knife4j</module>
|
||||
<module>common-redis</module>
|
||||
<module>common-web</module>
|
||||
<module>common-database</module>
|
||||
<module>common-mybatis</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
@ -118,7 +118,7 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>common-database</artifactId>
|
||||
<artifactId>common-mybatis</artifactId>
|
||||
<version>${youlai.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
@ -1,36 +0,0 @@
|
||||
package com.youlai.gateway.component;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.youlai.common.core.result.Result;
|
||||
import com.youlai.common.core.result.ResultCode;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 无权访问自定义响应
|
||||
*/
|
||||
@Component
|
||||
public class CustomServerAccessDeniedHandler implements ServerAccessDeniedHandler {
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException e) {
|
||||
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.USER_ACCESS_UNAUTHORIZED));
|
||||
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
|
||||
return response.writeWith(Mono.just(buffer));
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package com.youlai.gateway.component;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.youlai.common.core.result.Result;
|
||||
import com.youlai.common.core.result.ResultCode;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 无效token/token过期 自定义响应
|
||||
*/
|
||||
@Component
|
||||
public class CustomServerAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
|
||||
|
||||
@Override
|
||||
public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
|
||||
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.TOKEN_INVALID_OR_EXPIRED));
|
||||
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
|
||||
return response.writeWith(Mono.just(buffer));
|
||||
}
|
||||
|
||||
}
|
@ -1,25 +1,36 @@
|
||||
package com.youlai.gateway.config;
|
||||
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.youlai.common.core.constant.AuthConstants;
|
||||
import com.youlai.gateway.component.AuthorizationManager;
|
||||
import com.youlai.gateway.component.CustomServerAccessDeniedHandler;
|
||||
import com.youlai.gateway.component.CustomServerAuthenticationEntryPoint;
|
||||
import com.youlai.common.core.result.Result;
|
||||
import com.youlai.common.core.result.ResultCode;
|
||||
import com.youlai.gateway.security.AuthorizationManager;
|
||||
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.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 资源服务器配置
|
||||
*/
|
||||
@ -29,34 +40,79 @@ import reactor.core.publisher.Mono;
|
||||
public class ResourceServerConfig {
|
||||
|
||||
private AuthorizationManager authorizationManager;
|
||||
private CustomServerAccessDeniedHandler customServerAccessDeniedHandler;
|
||||
private CustomServerAuthenticationEntryPoint customServerAuthenticationEntryPoint;
|
||||
private WhiteListConfig whiteListConfig;
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
|
||||
http.oauth2ResourceServer().jwt()
|
||||
.jwtAuthenticationConverter(jwtAuthenticationConverter());
|
||||
// 自定义处理JWT请求头过期或签名错误的结果
|
||||
http.oauth2ResourceServer().authenticationEntryPoint(customServerAuthenticationEntryPoint);
|
||||
http.oauth2ResourceServer().authenticationEntryPoint(authenticationEntryPoint());
|
||||
http.authorizeExchange()
|
||||
.pathMatchers(ArrayUtil.toArray(whiteListConfig.getUrls(),String.class)).permitAll()
|
||||
.pathMatchers(ArrayUtil.toArray(whiteListConfig.getUrls(), String.class)).permitAll()
|
||||
.anyExchange().access(authorizationManager)
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.accessDeniedHandler(customServerAccessDeniedHandler) // 处理未授权
|
||||
.authenticationEntryPoint(customServerAuthenticationEntryPoint) //处理未认证
|
||||
.accessDeniedHandler(accessDeniedHandler()) // 处理未授权
|
||||
.authenticationEntryPoint(authenticationEntryPoint()) //处理未认证
|
||||
.and().csrf().disable();
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 未授权
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
ServerAccessDeniedHandler accessDeniedHandler() {
|
||||
return (exchange, denied) -> {
|
||||
Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
|
||||
.flatMap(response -> {
|
||||
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.USER_ACCESS_UNAUTHORIZED));
|
||||
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
|
||||
return response.writeWith(Mono.just(buffer))
|
||||
.doOnError(error -> DataBufferUtils.release(buffer));
|
||||
});
|
||||
|
||||
return mono;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* token无效或者已过期自定义响应
|
||||
*/
|
||||
@Bean
|
||||
ServerAuthenticationEntryPoint authenticationEntryPoint() {
|
||||
return (exchange, e) -> {
|
||||
Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
|
||||
.flatMap(response -> {
|
||||
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.TOKEN_INVALID_OR_EXPIRED));
|
||||
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
|
||||
return response.writeWith(Mono.just(buffer))
|
||||
.doOnError(error -> DataBufferUtils.release(buffer));
|
||||
});
|
||||
return mono;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @link https://blog.csdn.net/qq_24230139/article/details/105091273
|
||||
* ServerHttpSecurity没有将jwt中authorities的负载部分当做Authentication
|
||||
* 需要把jwt的Claim中的authorities加入
|
||||
* 方案:重新定义ReactiveAuthenticationManager权限管理器,默认转换器JwtGrantedAuthoritiesConverter
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
|
||||
@ -68,5 +124,4 @@ public class ResourceServerConfig {
|
||||
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
|
||||
return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.youlai.gateway.component;
|
||||
package com.youlai.gateway.security;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
Loading…
Reference in New Issue
Block a user