mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2024-12-22 12:48:59 +08:00
refactor: 授权服务器代码优化和注释完善
This commit is contained in:
parent
3fd84f84d6
commit
beb0f74d0f
@ -1,6 +1,7 @@
|
||||
package com.youlai.auth.authentication.captcha;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.auth.util.OAuth2AuthenticationProviderUtils;
|
||||
import com.youlai.common.constant.SecurityConstants;
|
||||
@ -120,6 +121,9 @@ public class CaptchaAuthenticationProvider implements AuthenticationProvider {
|
||||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||
|
||||
|
||||
// 权限数据比较多通过反射移除不持久化至数据库
|
||||
ReflectUtil.setFieldValue(usernamePasswordAuthentication.getPrincipal(), "perms", null);
|
||||
|
||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
|
||||
.principalName(usernamePasswordAuthentication.getName())
|
||||
.authorizationGrantType(CaptchaAuthenticationToken.CAPTCHA)
|
||||
@ -150,6 +154,7 @@ public class CaptchaAuthenticationProvider implements AuthenticationProvider {
|
||||
}
|
||||
|
||||
OAuth2Authorization authorization = authorizationBuilder.build();
|
||||
// 持久化令牌发放记录到数据库
|
||||
this.authorizationService.save(authorization);
|
||||
additionalParameters = Collections.EMPTY_MAP;
|
||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters);
|
||||
|
@ -128,14 +128,14 @@ public class PasswordAuthenticationProvider implements AuthenticationProvider {
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
|
||||
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
|
||||
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
|
||||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||
|
||||
|
||||
// 权限数据(perms)比较多通过反射移除,不随令牌一起持久化至数据库
|
||||
ReflectUtil.setFieldValue(usernamePasswordAuthentication.getPrincipal(), "perms", null);
|
||||
|
||||
// 持久化令牌发放记录到数据库
|
||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
|
||||
.principalName(usernamePasswordAuthentication.getName())
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
@ -166,10 +166,12 @@ public class PasswordAuthenticationProvider implements AuthenticationProvider {
|
||||
authorizationBuilder.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
|
||||
OAuth2Authorization authorization = authorizationBuilder.build();
|
||||
|
||||
// 持久化令牌发放记录到数据库
|
||||
this.authorizationService.save(authorization);
|
||||
additionalParameters = Collections.emptyMap();
|
||||
|
||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters);
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ public class AuthorizationServerConfig {
|
||||
private final OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer;
|
||||
|
||||
/**
|
||||
* 授权配置
|
||||
* 授权服务器端点配置
|
||||
*/
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@ -106,6 +106,7 @@ public class AuthorizationServerConfig {
|
||||
tokenEndpoint
|
||||
.accessTokenRequestConverters(
|
||||
authenticationConverters ->// <1>
|
||||
// 自定义授权模式转换器(Converter)
|
||||
authenticationConverters.addAll(
|
||||
List.of(
|
||||
new PasswordAuthenticationConverter(),
|
||||
@ -115,7 +116,8 @@ public class AuthorizationServerConfig {
|
||||
)
|
||||
)
|
||||
)
|
||||
.authenticationProviders(authenticationProviders ->// <2>
|
||||
.authenticationProviders(authenticationProviders ->// <2>
|
||||
// 自定义授权模式提供者(Provider)
|
||||
authenticationProviders.addAll(
|
||||
List.of(
|
||||
new PasswordAuthenticationProvider(authenticationManager, authorizationService, tokenGenerator),
|
||||
@ -126,7 +128,7 @@ public class AuthorizationServerConfig {
|
||||
)
|
||||
)
|
||||
.accessTokenResponseHandler(new MyAuthenticationSuccessHandler()) // 自定义成功响应
|
||||
.errorResponseHandler(new MyAuthenticationFailureHandler()) // 自定义异常响应
|
||||
.errorResponseHandler(new MyAuthenticationFailureHandler()) // 自定义失败响应
|
||||
);
|
||||
|
||||
|
||||
@ -140,15 +142,6 @@ public class AuthorizationServerConfig {
|
||||
}
|
||||
|
||||
|
||||
/* @Bean
|
||||
public AuthenticationProvider daoAuthenticationProvider() {
|
||||
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
|
||||
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
|
||||
daoAuthenticationProvider.setHideUserNotFoundExceptions(false) ; // 抛出用户不存在异常
|
||||
return daoAuthenticationProvider ;
|
||||
}*/
|
||||
|
||||
|
||||
@Bean // <5>
|
||||
public JWKSource<SecurityContext> jwkSource() {
|
||||
KeyPair keyPair = generateRsaKey();
|
||||
@ -261,8 +254,11 @@ public class AuthorizationServerConfig {
|
||||
String clientSecret = "123456";
|
||||
String clientName = "商城管理客户端";
|
||||
|
||||
// 如果使用明文,在客户端认证的时候会自动升级加密方式(修改密码), 直接使用 bcrypt 加密避免不必要的麻烦
|
||||
// 不开玩笑,官方ISSUE: https://github.com/spring-projects/spring-authorization-server/issues/1099
|
||||
|
||||
/*
|
||||
如果使用明文,客户端认证时会自动升级加密方式,换句话说直接修改客户端密码,所以直接使用 bcrypt 加密避免不必要的麻烦
|
||||
官方ISSUE: https://github.com/spring-projects/spring-authorization-server/issues/1099
|
||||
*/
|
||||
String encodeSecret = passwordEncoder().encode(clientSecret);
|
||||
|
||||
RegisteredClient registeredMallAdminClient = registeredClientRepository.findByClientId(clientId);
|
||||
|
@ -1,9 +1,5 @@
|
||||
package com.youlai.auth.config;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@ -14,17 +10,10 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class DefaultSecurityConfig {
|
||||
|
||||
/**
|
||||
* 读取 配置中的白名单
|
||||
*/
|
||||
@Setter
|
||||
private List<String> ignoreUrls;
|
||||
/**
|
||||
* Spring Security 安全过滤器链配置
|
||||
*
|
||||
@ -36,12 +25,7 @@ public class DefaultSecurityConfig {
|
||||
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeHttpRequests(requestMatcherRegistry ->
|
||||
{
|
||||
if (CollectionUtil.isNotEmpty(ignoreUrls)) {
|
||||
requestMatcherRegistry.requestMatchers(Convert.toStrArray(ignoreUrls)).permitAll();
|
||||
}
|
||||
requestMatcherRegistry.anyRequest().authenticated();
|
||||
}
|
||||
requestMatcherRegistry.anyRequest().authenticated()
|
||||
)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.formLogin(Customizer.withDefaults());
|
||||
|
@ -5,7 +5,6 @@ import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
@ -13,26 +12,26 @@ import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 认证异常处理器
|
||||
* 认证失败处理器
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2023/7/6
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
|
||||
|
||||
/**
|
||||
* MappingJackson2HttpMessageConverter 是 Spring 框架提供的一个 HTTP 消息转换器,用于将 HTTP 请求和响应的 JSON 数据与 Java 对象之间进行转换
|
||||
*/
|
||||
private final HttpMessageConverter<Object> accessTokenHttpResponseConverter = new MappingJackson2HttpMessageConverter();
|
||||
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
|
||||
log.warn(" authentication failure: ", exception);
|
||||
|
||||
OAuth2Error error = ((OAuth2AuthenticationException) exception).getError();
|
||||
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
|
||||
Result result = Result.failed(error.getErrorCode());
|
||||
|
@ -25,21 +25,26 @@ import java.util.Map;
|
||||
* 认证成功处理器
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2023/7/3
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
/**
|
||||
* MappingJackson2HttpMessageConverter 是 Spring 框架提供的一个 HTTP 消息转换器,用于将 HTTP 请求和响应的 JSON 数据与 Java 对象之间进行转换
|
||||
*/
|
||||
private final HttpMessageConverter<Object> accessTokenHttpResponseConverter = new MappingJackson2HttpMessageConverter();
|
||||
private Converter<OAuth2AccessTokenResponse, Map<String, Object>> accessTokenResponseParametersConverter = new DefaultOAuth2AccessTokenResponseMapConverter();
|
||||
|
||||
|
||||
/**
|
||||
* @param request the request which caused the successful authentication
|
||||
* @param response the response
|
||||
* 自定义认证成功响应数据结构
|
||||
*
|
||||
* @param request the request which caused the successful authentication
|
||||
* @param response the response
|
||||
* @param authentication the <tt>Authentication</tt> object which was created during
|
||||
* the authentication process.
|
||||
* the authentication process.
|
||||
* @throws IOException
|
||||
* @throws ServletException
|
||||
* @see org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter#sendAccessTokenResponse
|
||||
*/
|
||||
|
||||
@Override
|
||||
@ -68,7 +73,7 @@ public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHand
|
||||
Map<String, Object> tokenResponseParameters = this.accessTokenResponseParametersConverter
|
||||
.convert(accessTokenResponse);
|
||||
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
|
||||
// 自定义认证成功响应数据结构
|
||||
|
||||
this.accessTokenHttpResponseConverter.write(Result.success(tokenResponseParameters), null, httpResponse);
|
||||
}
|
||||
}
|
||||
|
@ -38,11 +38,10 @@ class SysUserDeserializer extends JsonDeserializer<SysUserDetails> {
|
||||
* @param jp the JsonParser
|
||||
* @param ctxt the DeserializationContext
|
||||
* @return the user
|
||||
* @throws IOException if a exception during IO occurs
|
||||
* @throws JsonProcessingException if an error during JSON processing occurs
|
||||
* @throws IOException if a exception during IO occurs
|
||||
*/
|
||||
@Override
|
||||
public SysUserDetails deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
|
||||
public SysUserDetails deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
|
||||
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
|
||||
JsonNode jsonNode = mapper.readTree(jp);
|
||||
Set<? extends GrantedAuthority> authorities = mapper.convertValue(jsonNode.get("authorities"),
|
||||
|
@ -29,18 +29,19 @@ public class PasswordAuthenticationTests {
|
||||
* 测试密码模式登录
|
||||
*/
|
||||
@Test
|
||||
void testPasswordAuthentication() throws Exception {
|
||||
void testPasswordLogin() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
// 客户端ID和密钥
|
||||
headers.setBasicAuth("mall-admin", "123456");
|
||||
|
||||
this.mvc.perform(post("/oauth2/token")
|
||||
.param(OAuth2ParameterNames.GRANT_TYPE, "password")
|
||||
.param(OAuth2ParameterNames.USERNAME, "admin")
|
||||
.param(OAuth2ParameterNames.PASSWORD, "123456")
|
||||
.param(OAuth2ParameterNames.GRANT_TYPE, "password") // 密码模式
|
||||
.param(OAuth2ParameterNames.USERNAME, "admin") // 用户名
|
||||
.param(OAuth2ParameterNames.PASSWORD, "123456") // 密码
|
||||
.headers(headers))
|
||||
.andDo(print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.access_token").isNotEmpty());
|
||||
.andExpect(jsonPath("$.data.access_token").isNotEmpty());
|
||||
}
|
||||
|
||||
|
||||
|
@ -108,7 +108,7 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
||||
* 路由列表
|
||||
*/
|
||||
@Override
|
||||
@Cacheable(cacheNames = "system", key = "'routes'")
|
||||
//@Cacheable(cacheNames = "system", key = "'routes'")
|
||||
public List<RouteVO> listRoutes() {
|
||||
List<RouteBO> menuList = this.baseMapper.listRoutes();
|
||||
return recurRoutes(SystemConstants.ROOT_NODE_ID, menuList);
|
||||
|
Loading…
Reference in New Issue
Block a user