Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
有来开源组织
youlai-mall
提交
4a943ab5
Y
youlai-mall
项目概览
有来开源组织
/
youlai-mall
通知
7
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Y
youlai-mall
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
4a943ab5
编写于
7月 07, 2023
作者:
郝
郝先瑞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 完善OAuth2自定义异常处理逻辑
上级
a07a3c88
变更
18
隐藏空白更改
内联
并排
Showing
18 changed file
with
231 addition
and
219 deletion
+231
-219
youlai-auth/pom.xml
youlai-auth/pom.xml
+0
-5
youlai-auth/src/main/java/com/youlai/auth/authentication/captcha/CaptchaAuthenticationProvider.java
...authentication/captcha/CaptchaAuthenticationProvider.java
+2
-1
youlai-auth/src/main/java/com/youlai/auth/authentication/miniapp/WxMiniAppAuthenticationConverter.java
...hentication/miniapp/WxMiniAppAuthenticationConverter.java
+1
-1
youlai-auth/src/main/java/com/youlai/auth/authentication/miniapp/WxMiniAppAuthenticationProvider.java
...thentication/miniapp/WxMiniAppAuthenticationProvider.java
+1
-1
youlai-auth/src/main/java/com/youlai/auth/authentication/miniapp/WxMiniAppAuthenticationToken.java
.../authentication/miniapp/WxMiniAppAuthenticationToken.java
+1
-1
youlai-auth/src/main/java/com/youlai/auth/authentication/password/PasswordAuthenticationConverter.java
...hentication/password/PasswordAuthenticationConverter.java
+2
-0
youlai-auth/src/main/java/com/youlai/auth/authentication/password/PasswordAuthenticationProvider.java
...thentication/password/PasswordAuthenticationProvider.java
+18
-6
youlai-auth/src/main/java/com/youlai/auth/config/AuthorizationServerConfig.java
...ava/com/youlai/auth/config/AuthorizationServerConfig.java
+45
-28
youlai-auth/src/main/java/com/youlai/auth/config/DefaultSecurityConfig.java
...in/java/com/youlai/auth/config/DefaultSecurityConfig.java
+8
-11
youlai-auth/src/main/java/com/youlai/auth/config/JwtTokenClaimsConfig.java
...ain/java/com/youlai/auth/config/JwtTokenClaimsConfig.java
+4
-5
youlai-auth/src/main/java/com/youlai/auth/controller/AuthController.java
.../main/java/com/youlai/auth/controller/AuthController.java
+2
-0
youlai-auth/src/main/java/com/youlai/auth/handler/MyAuthenticationFailureHandler.java
...m/youlai/auth/handler/MyAuthenticationFailureHandler.java
+42
-0
youlai-auth/src/main/java/com/youlai/auth/handler/MyAuthenticationSuccessHandler.java
...m/youlai/auth/handler/MyAuthenticationSuccessHandler.java
+13
-14
youlai-auth/src/main/java/com/youlai/auth/userdetails/user/SysUserDetails.java
...java/com/youlai/auth/userdetails/user/SysUserDetails.java
+39
-2
youlai-auth/src/main/java/com/youlai/auth/userdetails/user/SysUserDetailsService.java
...m/youlai/auth/userdetails/user/SysUserDetailsService.java
+6
-19
youlai-auth/src/main/java/com/youlai/auth/userdetails/user/jackson/SysUserDeserializer.java
...ai/auth/userdetails/user/jackson/SysUserDeserializer.java
+42
-55
youlai-auth/src/main/java/com/youlai/auth/userdetails/user/jackson/SysUserMixin.java
...om/youlai/auth/userdetails/user/jackson/SysUserMixin.java
+5
-2
youlai-auth/src/main/java/com/youlai/auth/util/RequestUtils.java
...auth/src/main/java/com/youlai/auth/util/RequestUtils.java
+0
-68
未找到文件。
youlai-auth/pom.xml
浏览文件 @
4a943ab5
...
...
@@ -43,11 +43,6 @@
</dependency>
<!-- OAuth2 认证服务器-->
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-security
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.security
</groupId>
<artifactId>
spring-security-oauth2-authorization-server
</artifactId>
...
...
youlai-auth/src/main/java/com/youlai/auth/authentication/captcha/CaptchaAuthenticationProvider.java
浏览文件 @
4a943ab5
...
...
@@ -94,7 +94,7 @@ public class CaptchaAuthenticationProvider implements AuthenticationProvider {
String
username
=
(
String
)
additionalParameters
.
get
(
OAuth2ParameterNames
.
USERNAME
);
String
password
=
(
String
)
additionalParameters
.
get
(
OAuth2ParameterNames
.
PASSWORD
);
UsernamePasswordAuthenticationToken
usernamePasswordAuthenticationToken
=
new
UsernamePasswordAuthenticationToken
(
username
,
password
);
// 用户名密码身份验证,成功后返回带有权限的认证信息
// 用户名密码身份验证,成功后返回
带有权限的认证信息
Authentication
usernamePasswordAuthentication
=
authenticationManager
.
authenticate
(
usernamePasswordAuthenticationToken
);
// 访问令牌(Access Token) 构造器
...
...
@@ -119,6 +119,7 @@ public class CaptchaAuthenticationProvider implements AuthenticationProvider {
generatedAccessToken
.
getTokenValue
(),
generatedAccessToken
.
getIssuedAt
(),
generatedAccessToken
.
getExpiresAt
(),
tokenContext
.
getAuthorizedScopes
());
OAuth2Authorization
.
Builder
authorizationBuilder
=
OAuth2Authorization
.
withRegisteredClient
(
registeredClient
)
.
principalName
(
usernamePasswordAuthentication
.
getName
())
.
authorizationGrantType
(
CaptchaAuthenticationToken
.
CAPTCHA
)
...
...
youlai-auth/src/main/java/com/youlai/auth/authentication/
wx
miniapp/WxMiniAppAuthenticationConverter.java
→
youlai-auth/src/main/java/com/youlai/auth/authentication/miniapp/WxMiniAppAuthenticationConverter.java
浏览文件 @
4a943ab5
package
com.youlai.auth.authentication.
wx
miniapp
;
package
com.youlai.auth.authentication.miniapp
;
import
cn.hutool.core.util.StrUtil
;
import
com.youlai.auth.util.OAuth2EndpointUtils
;
...
...
youlai-auth/src/main/java/com/youlai/auth/authentication/
wx
miniapp/WxMiniAppAuthenticationProvider.java
→
youlai-auth/src/main/java/com/youlai/auth/authentication/miniapp/WxMiniAppAuthenticationProvider.java
浏览文件 @
4a943ab5
package
com.youlai.auth.authentication.
wx
miniapp
;
package
com.youlai.auth.authentication.miniapp
;
import
cn.binarywang.wx.miniapp.api.WxMaService
;
import
cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult
;
...
...
youlai-auth/src/main/java/com/youlai/auth/authentication/
wx
miniapp/WxMiniAppAuthenticationToken.java
→
youlai-auth/src/main/java/com/youlai/auth/authentication/miniapp/WxMiniAppAuthenticationToken.java
浏览文件 @
4a943ab5
package
com.youlai.auth.authentication.
wx
miniapp
;
package
com.youlai.auth.authentication.miniapp
;
import
jakarta.annotation.Nullable
;
import
org.springframework.security.core.Authentication
;
...
...
youlai-auth/src/main/java/com/youlai/auth/authentication/password/PasswordAuthenticationConverter.java
浏览文件 @
4a943ab5
...
...
@@ -2,6 +2,7 @@ package com.youlai.auth.authentication.password;
import
cn.hutool.core.util.StrUtil
;
import
com.youlai.auth.util.OAuth2EndpointUtils
;
import
org.springframework.security.authentication.InternalAuthenticationServiceException
;
import
org.springframework.security.core.Authentication
;
import
org.springframework.security.core.context.SecurityContextHolder
;
import
org.springframework.security.oauth2.core.AuthorizationGrantType
;
...
...
@@ -32,6 +33,7 @@ public class PasswordAuthenticationConverter implements AuthenticationConverter
@Override
public
Authentication
convert
(
HttpServletRequest
request
)
{
// 授权类型 (必需)
String
grantType
=
request
.
getParameter
(
OAuth2ParameterNames
.
GRANT_TYPE
);
if
(!
AuthorizationGrantType
.
PASSWORD
.
getValue
().
equals
(
grantType
))
{
...
...
youlai-auth/src/main/java/com/youlai/auth/authentication/password/PasswordAuthenticationProvider.java
浏览文件 @
4a943ab5
...
...
@@ -2,13 +2,15 @@ package com.youlai.auth.authentication.password;
import
cn.hutool.core.lang.Assert
;
import
cn.hutool.core.util.ReflectUtil
;
import
com.youlai.auth.util.OAuth2AuthenticationProviderUtils
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.security.authentication.AuthenticationManager
;
import
org.springframework.security.authentication.AuthenticationProvider
;
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
;
import
org.springframework.security.authentication.*
;
import
org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider
;
import
org.springframework.security.core.Authentication
;
import
org.springframework.security.core.AuthenticationException
;
import
org.springframework.security.core.userdetails.UserDetails
;
import
org.springframework.security.core.userdetails.UserDetailsChecker
;
import
org.springframework.security.oauth2.core.*
;
import
org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames
;
import
org.springframework.security.oauth2.core.oidc.OidcIdToken
;
...
...
@@ -35,7 +37,6 @@ import java.util.stream.Collectors;
* 密码模式身份验证提供者
* <p>
* 处理基于用户名和密码的身份验证
*
* @author haoxr
* @since 3.0.0
*/
...
...
@@ -86,7 +87,12 @@ public class PasswordAuthenticationProvider implements AuthenticationProvider {
Map
<
String
,
Object
>
additionalParameters
=
resourceOwnerPasswordAuthentication
.
getAdditionalParameters
();
String
username
=
(
String
)
additionalParameters
.
get
(
OAuth2ParameterNames
.
USERNAME
);
String
password
=
(
String
)
additionalParameters
.
get
(
OAuth2ParameterNames
.
PASSWORD
);
UsernamePasswordAuthenticationToken
usernamePasswordAuthenticationToken
=
new
UsernamePasswordAuthenticationToken
(
username
,
password
);
// 这种
// https://github.com/Basit-Mahmood/spring-authorization-server-password-grant-type-support/blob/master/SpringAuthorizationServer/src/main/java/pk/training/basit/oauth2/authentication/OAuth2ResourceOwnerPasswordAuthenticationProvider.java
UsernamePasswordAuthenticationToken
usernamePasswordAuthenticationToken
=
new
UsernamePasswordAuthenticationToken
(
username
,
password
);
// 用户名密码身份验证,成功后返回带有权限的认证信息
Authentication
usernamePasswordAuthentication
=
authenticationManager
.
authenticate
(
usernamePasswordAuthenticationToken
);
...
...
@@ -126,11 +132,15 @@ public class PasswordAuthenticationProvider implements AuthenticationProvider {
generatedAccessToken
.
getTokenValue
(),
generatedAccessToken
.
getIssuedAt
(),
generatedAccessToken
.
getExpiresAt
(),
tokenContext
.
getAuthorizedScopes
());
ReflectUtil
.
setFieldValue
(
usernamePasswordAuthentication
.
getPrincipal
(),
"perms"
,
null
);
// 持久化令牌发放记录到数据库
OAuth2Authorization
.
Builder
authorizationBuilder
=
OAuth2Authorization
.
withRegisteredClient
(
registeredClient
)
.
principalName
(
usernamePasswordAuthentication
.
getName
())
.
authorizationGrantType
(
AuthorizationGrantType
.
PASSWORD
)
.
authorizedScopes
(
authorizedScopes
)
.
attribute
(
Principal
.
class
.
getName
(),
usernamePasswordAuthentication
);
.
attribute
(
Principal
.
class
.
getName
(),
usernamePasswordAuthentication
);
// attribute 字段
if
(
generatedAccessToken
instanceof
ClaimAccessor
)
{
authorizationBuilder
.
token
(
accessToken
,
(
metadata
)
->
metadata
.
put
(
OAuth2Authorization
.
Token
.
CLAIMS_METADATA_NAME
,
((
ClaimAccessor
)
generatedAccessToken
).
getClaims
()));
...
...
@@ -197,4 +207,6 @@ public class PasswordAuthenticationProvider implements AuthenticationProvider {
return
PasswordAuthenticationToken
.
class
.
isAssignableFrom
(
authentication
);
}
}
youlai-auth/src/main/java/com/youlai/auth/config/AuthorizationServerConfig.java
浏览文件 @
4a943ab5
...
...
@@ -2,6 +2,8 @@
package
com.youlai.auth.config
;
import
cn.binarywang.wx.miniapp.api.WxMaService
;
import
cn.hutool.core.collection.CollectionUtil
;
import
cn.hutool.core.convert.Convert
;
import
com.fasterxml.jackson.databind.Module
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.nimbusds.jose.jwk.JWKSet
;
...
...
@@ -17,14 +19,16 @@ import com.youlai.auth.authentication.password.PasswordAuthenticationProvider;
import
com.youlai.auth.authentication.smscode.SmsCodeAuthenticationConverter
;
import
com.youlai.auth.authentication.smscode.SmsCodeAuthenticationProvider
;
import
com.youlai.auth.authentication.smscode.SmsCodeAuthenticationToken
;
import
com.youlai.auth.authentication.wxminiapp.WxMiniAppAuthenticationConverter
;
import
com.youlai.auth.authentication.wxminiapp.WxMiniAppAuthenticationProvider
;
import
com.youlai.auth.authentication.wxminiapp.WxMiniAppAuthenticationToken
;
import
com.youlai.auth.authentication.miniapp.WxMiniAppAuthenticationConverter
;
import
com.youlai.auth.authentication.miniapp.WxMiniAppAuthenticationProvider
;
import
com.youlai.auth.authentication.miniapp.WxMiniAppAuthenticationToken
;
import
com.youlai.auth.handler.MyAuthenticationFailureHandler
;
import
com.youlai.auth.handler.MyAuthenticationSuccessHandler
;
import
com.youlai.auth.userdetails.member.MemberDetailsService
;
import
com.youlai.auth.userdetails.user.SysUserDetails
;
import
com.youlai.auth.userdetails.user.jackson.SysUseMixin
;
import
com.youlai.auth.userdetails.user.jackson.SysUse
r
Mixin
;
import
lombok.RequiredArgsConstructor
;
import
lombok.Setter
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
...
...
@@ -34,8 +38,10 @@ import org.springframework.data.redis.core.StringRedisTemplate;
import
org.springframework.jdbc.core.JdbcTemplate
;
import
org.springframework.jdbc.support.lob.DefaultLobHandler
;
import
org.springframework.security.authentication.AuthenticationManager
;
import
org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
;
import
org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity
;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer
;
import
org.springframework.security.crypto.factory.PasswordEncoderFactories
;
import
org.springframework.security.crypto.password.PasswordEncoder
;
import
org.springframework.security.jackson2.SecurityJackson2Modules
;
...
...
@@ -98,22 +104,23 @@ public class AuthorizationServerConfig {
)
throws
Exception
{
OAuth2AuthorizationServerConfigurer
authorizationServerConfigurer
=
new
OAuth2AuthorizationServerConfigurer
();
OAuth2AuthorizationServerConfigurer
authorizationServerConfigurer
=
new
OAuth2AuthorizationServerConfigurer
();
authorizationServerConfigurer
.
tokenEndpoint
(
tokenEndpoint
->
tokenEndpoint
.
accessTokenRequestConverters
(
authenticationConverters
->
// <1>
authenticationConverters
.
addAll
(
List
.
of
(
new
PasswordAuthenticationConverter
(),
new
CaptchaAuthenticationConverter
(),
new
WxMiniAppAuthenticationConverter
(),
new
SmsCodeAuthenticationConverter
()
.
accessTokenRequestConverters
(
authenticationConverters
->
// <1>
authenticationConverters
.
addAll
(
List
.
of
(
new
PasswordAuthenticationConverter
(),
new
CaptchaAuthenticationConverter
(),
new
WxMiniAppAuthenticationConverter
(),
new
SmsCodeAuthenticationConverter
()
)
)
)
)
.
authenticationProviders
(
authenticationProviders
->
// <2>
/*
.authenticationProviders(authenticationProviders ->// <2>
authenticationProviders.addAll(
List.of(
new PasswordAuthenticationProvider(authenticationManager, authorizationService, tokenGenerator),
...
...
@@ -122,25 +129,37 @@ public class AuthorizationServerConfig {
new SmsCodeAuthenticationProvider(authorizationService, tokenGenerator, memberDetailsService, redisTemplate)
)
)
)
.
accessTokenResponseHandler
(
new
MyAuthenticationSuccessHandler
())
)*/
.
accessTokenResponseHandler
(
new
MyAuthenticationSuccessHandler
())
// 自定义成功响应
.
errorResponseHandler
(
new
MyAuthenticationFailureHandler
())
// 自定义异常响应
);
RequestMatcher
endpointsMatcher
=
authorizationServerConfigurer
.
getEndpointsMatcher
();
http
.
securityMatcher
(
endpointsMatcher
)
.
authorizeHttpRequests
(
authorize
->
authorize
.
anyRequest
().
authenticated
()
)
RequestMatcher
endpointsMatcher
=
authorizationServerConfigurer
.
getEndpointsMatcher
();
http
.
securityMatcher
(
endpointsMatcher
)
.
authorizeHttpRequests
(
authorizeRequests
->
authorizeRequests
.
anyRequest
().
authenticated
())
.
csrf
(
csrf
->
csrf
.
ignoringRequestMatchers
(
endpointsMatcher
))
.
apply
(
authorizationServerConfigurer
);
AuthenticationManagerBuilder
authenticationManagerBuilder
=
http
.
getSharedObject
(
AuthenticationManagerBuilder
.
class
);
authenticationManagerBuilder
.
parentAuthenticationManager
(
null
);
authenticationManagerBuilder
.
authenticationProvider
(
new
PasswordAuthenticationProvider
(
authenticationManager
,
authorizationService
,
tokenGenerator
));
return
http
.
build
();
}
/* @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
();
...
...
@@ -208,7 +227,7 @@ public class AuthorizationServerConfig {
objectMapper
.
registerModules
(
securityModules
);
objectMapper
.
registerModule
(
new
OAuth2AuthorizationServerJackson2Module
());
// You will need to write the Mixin for your class so Jackson can marshall it.
objectMapper
.
addMixIn
(
SysUserDetails
.
class
,
SysUseMixin
.
class
);
objectMapper
.
addMixIn
(
SysUserDetails
.
class
,
SysUse
r
Mixin
.
class
);
objectMapper
.
addMixIn
(
Long
.
class
,
Object
.
class
);
rowMapper
.
setObjectMapper
(
objectMapper
);
service
.
setAuthorizationRowMapper
(
rowMapper
);
...
...
@@ -236,11 +255,9 @@ public class AuthorizationServerConfig {
}
@Bean
public
AuthenticationManager
authenticationManager
(
AuthenticationConfiguration
authenticationConfiguration
)
throws
Exception
{
return
authenticationConfiguration
.
getAuthenticationManager
();
}
...
...
youlai-auth/src/main/java/com/youlai/auth/config/SecurityConfig.java
→
youlai-auth/src/main/java/com/youlai/auth/config/
Default
SecurityConfig.java
浏览文件 @
4a943ab5
...
...
@@ -6,26 +6,22 @@ import lombok.RequiredArgsConstructor;
import
lombok.Setter
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.core.annotation.Order
;
import
org.springframework.security.config.Customizer
;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity
;
import
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer
;
import
org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer
;
import
org.springframework.security.crypto.factory.PasswordEncoderFactories
;
import
org.springframework.security.crypto.password.PasswordEncoder
;
import
org.springframework.security.web.SecurityFilterChain
;
import
java.util.List
;
import
static
org
.
springframework
.
security
.
config
.
Customizer
.
withDefaults
;
@Configuration
@EnableWebSecurity
(
debug
=
true
)
@RequiredArgsConstructor
public
class
SecurityConfig
{
@EnableWebSecurity
@Configuration
(
proxyBeanMethods
=
false
)
public
class
DefaultSecurityConfig
{
@Setter
private
List
<
String
>
ignoreUrls
;
/**
* Spring Security 安全过滤器链配置
*
...
...
@@ -33,6 +29,7 @@ public class SecurityConfig {
* @return
*/
@Bean
@Order
(
0
)
SecurityFilterChain
defaultSecurityFilterChain
(
HttpSecurity
http
)
throws
Exception
{
http
.
authorizeHttpRequests
(
requestMatcherRegistry
->
...
...
@@ -45,8 +42,8 @@ public class SecurityConfig {
}
)
.
csrf
(
AbstractHttpConfigurer:
:
disable
)
.
formLogin
(
withDefaults
())
;
.
formLogin
(
Customizer
.
withDefaults
());
return
http
.
build
();
}
...
...
youlai-auth/src/main/java/com/youlai/auth/config/Jwt
Customizer
Config.java
→
youlai-auth/src/main/java/com/youlai/auth/config/Jwt
TokenClaims
Config.java
浏览文件 @
4a943ab5
...
...
@@ -8,7 +8,6 @@ import org.springframework.context.annotation.Bean;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.data.redis.core.RedisTemplate
;
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
;
import
org.springframework.security.core.GrantedAuthority
;
import
org.springframework.security.core.authority.AuthorityUtils
;
import
org.springframework.security.core.userdetails.User
;
import
org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames
;
...
...
@@ -17,7 +16,6 @@ import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import
org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext
;
import
org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Optional
;
import
java.util.Set
;
...
...
@@ -27,16 +25,17 @@ import java.util.stream.Collectors;
* 自定义 JWT 的 Claims(声明)
*
* @author haoxr
* @see <a href="https://github.com/spring-projects/spring-authorization-server/pull/1264">How-to: Authorize an access token containing custom authorities</a>
* @since 2023/7/4
*/
@Configuration
@RequiredArgsConstructor
public
class
Jwt
Customizer
Config
{
public
class
Jwt
TokenClaims
Config
{
private
final
RedisTemplate
redisTemplate
;
@Bean
public
OAuth2TokenCustomizer
<
JwtEncodingContext
>
jwtCustomizer
()
{
public
OAuth2TokenCustomizer
<
JwtEncodingContext
>
jwt
Token
Customizer
()
{
return
context
->
{
if
(
OAuth2TokenType
.
ACCESS_TOKEN
.
equals
(
context
.
getTokenType
())
&&
context
.
getPrincipal
()
instanceof
UsernamePasswordAuthenticationToken
)
{
// Customize headers/claims for access_token
...
...
@@ -59,7 +58,7 @@ public class JwtCustomizerConfig {
}
else
if
(
principal
instanceof
MemberDetails
userDetails
)
{
claims
.
claim
(
"member_id"
,
String
.
valueOf
(
userDetails
.
getId
()));
}
else
{
}
else
{
User
user
=
(
User
)
principal
;
// 这里存入角色至JWT,解析JWT的角色用于鉴权的位置: ResourceServerConfig#jwtAuthenticationConverter
...
...
youlai-auth/src/main/java/com/youlai/auth/controller/AuthController.java
浏览文件 @
4a943ab5
...
...
@@ -3,6 +3,8 @@ package com.youlai.auth.controller;
import
org.springframework.web.bind.annotation.RestController
;
/**
* 认证控制器
*
* @author haoxr
* @since 2023/6/29
*/
...
...
youlai-auth/src/main/java/com/youlai/auth/handler/MyAuthenticationFailureHandler.java
0 → 100644
浏览文件 @
4a943ab5
package
com.youlai.auth.handler
;
import
com.youlai.common.result.Result
;
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
;
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
*/
@Slf4j
public
class
MyAuthenticationFailureHandler
implements
AuthenticationFailureHandler
{
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
);
httpResponse
.
setStatusCode
(
HttpStatus
.
OK
);
Result
result
=
Result
.
failed
(
error
.
getDescription
());
accessTokenHttpResponseConverter
.
write
(
result
,
null
,
httpResponse
);
}
}
youlai-auth/src/main/java/com/youlai/auth/handler/MyAuthenticationSuccessHandler.java
浏览文件 @
4a943ab5
package
com.youlai.auth.handler
;
import
cn.hutool.json.JSONUtil
;
import
com.youlai.common.result.Result
;
import
jakarta.servlet.ServletException
;
import
jakarta.servlet.http.HttpServletRequest
;
import
jakarta.servlet.http.HttpServletResponse
;
import
org.springframework.core.convert.converter.Converter
;
import
org.springframework.http.converter.HttpMessageConverter
;
import
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
;
import
org.springframework.http.server.ServletServerHttpResponse
;
import
org.springframework.security.core.Authentication
;
import
org.springframework.security.oauth2.core.OAuth2AccessToken
;
import
org.springframework.security.oauth2.core.OAuth2RefreshToken
;
import
org.springframework.security.oauth2.core.endpoint.DefaultOAuth2AccessTokenResponseMapConverter
;
import
org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse
;
import
org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter
;
import
org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken
;
import
org.springframework.security.web.authentication.AuthenticationSuccessHandler
;
import
org.springframework.util.CollectionUtils
;
...
...
@@ -22,23 +22,24 @@ import java.time.temporal.ChronoUnit;
import
java.util.Map
;
/**
* 认证成功处理器
*
* @author haoxr
* @since 2023/7/3
*/
public
class
MyAuthenticationSuccessHandler
implements
AuthenticationSuccessHandler
{
private
final
HttpMessageConverter
<
OAuth2AccessTokenResponse
>
accessTokenHttpResponseConverter
=
new
OAuth2AccessTokenResponseHttpMessageConverter
();
private
final
HttpMessageConverter
<
Object
>
accessTokenHttpResponseConverter
=
new
MappingJackson2HttpMessageConverter
();
private
Converter
<
OAuth2AccessTokenResponse
,
Map
<
String
,
Object
>>
accessTokenResponseParametersConverter
=
new
DefaultOAuth2AccessTokenResponseMapConverter
();
/**
*
* @see org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter#sendAccessTokenResponse
* @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
...
...
@@ -66,10 +67,8 @@ public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHand
Map
<
String
,
Object
>
tokenResponseParameters
=
this
.
accessTokenResponseParametersConverter
.
convert
(
accessTokenResponse
);
response
.
setCharacterEncoding
(
"UTF-8"
);
response
.
getWriter
().
write
(
JSONUtil
.
toJsonStr
(
Result
.
success
(
tokenResponseParameters
)));
ServletServerHttpResponse
httpResponse
=
new
ServletServerHttpResponse
(
response
);
// 自定义认证成功响应数据结构
this
.
accessTokenHttpResponseConverter
.
write
(
Result
.
success
(
tokenResponseParameters
),
null
,
httpResponse
);
}
}
youlai-auth/src/main/java/com/youlai/auth/userdetails/user/SysUserDetails.java
浏览文件 @
4a943ab5
...
...
@@ -4,11 +4,14 @@ import cn.hutool.core.collection.CollectionUtil;
import
com.youlai.common.enums.StatusEnum
;
import
com.youlai.system.dto.UserAuthInfo
;
import
lombok.Data
;
import
org.springframework.security.core.CredentialsContainer
;
import
org.springframework.security.core.GrantedAuthority
;
import
org.springframework.security.core.authority.SimpleGrantedAuthority
;
import
org.springframework.security.core.userdetails.UserDetails
;
import
org.springframework.util.Assert
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Set
;
import
java.util.stream.Collectors
;
...
...
@@ -22,7 +25,7 @@ import java.util.stream.Collectors;
* @since 3.0.0
*/
@Data
public
class
SysUserDetails
implements
UserDetails
{
public
class
SysUserDetails
implements
UserDetails
,
CredentialsContainer
{
/**
* 扩展字段:用户ID
...
...
@@ -45,7 +48,13 @@ public class SysUserDetails implements UserDetails {
private
String
username
;
private
String
password
;
private
Boolean
enabled
;
private
Collection
<
SimpleGrantedAuthority
>
authorities
;
private
Collection
<
GrantedAuthority
>
authorities
;
private
boolean
accountNonExpired
;
private
boolean
accountNonLocked
;
private
boolean
credentialsNonExpired
;
private
Set
<
String
>
perms
;
...
...
@@ -67,6 +76,28 @@ public class SysUserDetails implements UserDetails {
this
.
setPerms
(
user
.
getPerms
());
}
public
SysUserDetails
(
Long
userId
,
String
username
,
String
password
,
boolean
enabled
,
boolean
accountNonExpired
,
boolean
credentialsNonExpired
,
boolean
accountNonLocked
,
Set
<?
extends
GrantedAuthority
>
authorities
)
{
Assert
.
isTrue
(
username
!=
null
&&
!
""
.
equals
(
username
)
&&
password
!=
null
,
"Cannot pass null or empty values to constructor"
);
this
.
userId
=
userId
;
this
.
username
=
username
;
this
.
password
=
password
;
this
.
enabled
=
enabled
;
this
.
accountNonExpired
=
accountNonExpired
;
this
.
credentialsNonExpired
=
credentialsNonExpired
;
this
.
accountNonLocked
=
accountNonLocked
;
this
.
authorities
=
Collections
.
unmodifiableSet
(
authorities
);
}
@Override
public
Collection
<?
extends
GrantedAuthority
>
getAuthorities
()
{
return
this
.
authorities
;
...
...
@@ -101,4 +132,10 @@ public class SysUserDetails implements UserDetails {
public
boolean
isEnabled
()
{
return
this
.
enabled
;
}
@Override
public
void
eraseCredentials
()
{
this
.
password
=
null
;
}
}
youlai-auth/src/main/java/com/youlai/auth/userdetails/user/SysUserDetailsService.java
浏览文件 @
4a943ab5
package
com.youlai.auth.userdetails.user
;
import
cn.hutool.core.
lang.Assert
;
import
cn.hutool.core.
util.StrUtil
;
import
com.youlai.common.result.Result
;
import
com.youlai.system.api.UserFeignClient
;
import
com.youlai.system.dto.UserAuthInfo
;
import
lombok.RequiredArgsConstructor
;
import
org.springframework.context.annotation.Primary
;
import
org.springframework.security.authentication.AccountExpiredException
;
import
org.springframework.security.authentication.DisabledException
;
import
org.springframework.security.authentication.LockedException
;
import
org.springframework.security.core.userdetails.UserDetails
;
import
org.springframework.security.core.userdetails.UserDetailsService
;
import
org.springframework.security.core.userdetails.UsernameNotFoundException
;
import
org.springframework.stereotype.Service
;
/**
...
...
@@ -30,26 +27,16 @@ public class SysUserDetailsService implements UserDetailsService {
* <p>
* 用户名、密码用于后续认证,认证成功之后将权限授予用户
*
* @param username
前端登录表单的
用户名
* @param username 用户名
* @return {@link SysUserDetails}
*/
@Override
public
UserDetails
loadUserByUsername
(
String
username
)
{
Result
<
UserAuthInfo
>
result
=
userFeignClient
.
getUserAuthInfo
(
username
);
UserAuthInfo
userAuthInfo
=
null
;
Assert
.
isTrue
(
Result
.
isSuccess
(
result
)
&&
(
userAuthInfo
=
result
.
getData
())
!=
null
,
"用户不存在"
);
SysUserDetails
userDetails
=
new
SysUserDetails
(
userAuthInfo
);
if
(!
userDetails
.
isEnabled
())
{
throw
new
DisabledException
(
"该账户已被禁用!"
);
}
else
if
(!
userDetails
.
isAccountNonLocked
())
{
throw
new
LockedException
(
"该账号已被锁定!"
);
}
else
if
(!
userDetails
.
isAccountNonExpired
())
{
throw
new
AccountExpiredException
(
"该账号已过期!"
);
if
(!
Result
.
isSuccess
(
result
)
||
result
.
getData
()
==
null
)
{
throw
new
UsernameNotFoundException
(
StrUtil
.
format
(
"用户{}不存在"
,
username
));
}
return
userDetails
;
return
new
SysUserDetails
(
result
.
getData
())
;
}
}
youlai-auth/src/main/java/com/youlai/auth/userdetails/user/jackson/SysUserDeserializer.java
浏览文件 @
4a943ab5
/*
* Copyright 2015-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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.youlai.auth.userdetails.user.jackson
;
import
com.fasterxml.jackson.core.JsonParser
;
...
...
@@ -24,6 +8,7 @@ import com.fasterxml.jackson.databind.JsonDeserializer;
import
com.fasterxml.jackson.databind.JsonNode
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.node.MissingNode
;
import
com.youlai.auth.userdetails.user.SysUserDetails
;
import
org.springframework.security.core.GrantedAuthority
;
import
org.springframework.security.core.authority.SimpleGrantedAuthority
;
import
org.springframework.security.core.userdetails.User
;
...
...
@@ -33,51 +18,53 @@ import java.util.Set;
/**
* Custom Deserializer for {@link User} class. This is already registered with
* {@link UserMixin}. You can also use it directly with your mixin class.
* {@link
Sys
UserMixin}. You can also use it directly with your mixin class.
*
* @author Jitendra Singh
* @see SysUserMixin
* @since 4.2
* @see UserMixin
*/
class
SysUserDeserializer
extends
JsonDeserializer
<
User
>
{
class
SysUserDeserializer
extends
JsonDeserializer
<
SysUserDetails
>
{
private
static
final
TypeReference
<
Set
<
SimpleGrantedAuthority
>>
SIMPLE_GRANTED_AUTHORITY_SET
=
new
TypeReference
<
Set
<
SimpleGrantedAuthority
>>()
{
};
private
static
final
TypeReference
<
Set
<
SimpleGrantedAuthority
>>
SIMPLE_GRANTED_AUTHORITY_SET
=
new
TypeReference
<
Set
<
SimpleGrantedAuthority
>>()
{
};
/**
* This method will create {@link User} object. It will ensure successful object
* creation even if password key is null in serialized json, because credentials may
* be removed from the {@link User} by invoking {@link User#eraseCredentials()}. In
* that case there won't be any password key in serialized json.
* @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
*/
@Override
public
User
deserialize
(
JsonParser
jp
,
DeserializationContext
ctxt
)
throws
IOException
,
JsonProcessingException
{
ObjectMapper
mapper
=
(
ObjectMapper
)
jp
.
getCodec
();
JsonNode
jsonNode
=
mapper
.
readTree
(
jp
);
Set
<?
extends
GrantedAuthority
>
authorities
=
mapper
.
convertValue
(
jsonNode
.
get
(
"authorities"
),
SIMPLE_GRANTED_AUTHORITY_SET
);
JsonNode
passwordNode
=
readJsonNode
(
jsonNode
,
"password"
);
String
username
=
readJsonNode
(
jsonNode
,
"username"
).
asText
();
String
password
=
passwordNode
.
asText
(
""
);
boolean
enabled
=
readJsonNode
(
jsonNode
,
"enabled"
).
asBoolean
();
boolean
accountNonExpired
=
readJsonNode
(
jsonNode
,
"accountNonExpired"
).
asBoolean
();
boolean
credentialsNonExpired
=
readJsonNode
(
jsonNode
,
"credentialsNonExpired"
).
asBoolean
();
boolean
accountNonLocked
=
readJsonNode
(
jsonNode
,
"accountNonLocked"
).
asBoolean
();
User
result
=
new
User
(
username
,
password
,
enabled
,
accountNonExpired
,
credentialsNonExpired
,
accountNonLocked
,
authorities
);
if
(
passwordNode
.
asText
(
null
)
==
null
)
{
result
.
eraseCredentials
();
}
return
result
;
}
/**
* This method will create {@link User} object. It will ensure successful object
* creation even if password key is null in serialized json, because credentials may
* be removed from the {@link User} by invoking {@link User#eraseCredentials()}. In
* that case there won't be any password key in serialized json.
*
* @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
*/
@Override
public
SysUserDetails
deserialize
(
JsonParser
jp
,
DeserializationContext
ctxt
)
throws
IOException
,
JsonProcessingException
{
ObjectMapper
mapper
=
(
ObjectMapper
)
jp
.
getCodec
();
JsonNode
jsonNode
=
mapper
.
readTree
(
jp
);
Set
<?
extends
GrantedAuthority
>
authorities
=
mapper
.
convertValue
(
jsonNode
.
get
(
"authorities"
),
SIMPLE_GRANTED_AUTHORITY_SET
);
JsonNode
passwordNode
=
readJsonNode
(
jsonNode
,
"password"
);
Long
userId
=
readJsonNode
(
jsonNode
,
"userId"
).
asLong
();
String
username
=
readJsonNode
(
jsonNode
,
"username"
).
asText
();
String
password
=
passwordNode
.
asText
(
""
);
boolean
enabled
=
readJsonNode
(
jsonNode
,
"enabled"
).
asBoolean
();
boolean
accountNonExpired
=
readJsonNode
(
jsonNode
,
"accountNonExpired"
).
asBoolean
();
boolean
credentialsNonExpired
=
readJsonNode
(
jsonNode
,
"credentialsNonExpired"
).
asBoolean
();
boolean
accountNonLocked
=
readJsonNode
(
jsonNode
,
"accountNonLocked"
).
asBoolean
();
SysUserDetails
result
=
new
SysUserDetails
(
userId
,
username
,
password
,
enabled
,
accountNonExpired
,
credentialsNonExpired
,
accountNonLocked
,
authorities
);
if
(
passwordNode
.
asText
(
null
)
==
null
)
{
result
.
eraseCredentials
();
}
return
result
;
}
private
JsonNode
readJsonNode
(
JsonNode
jsonNode
,
String
field
)
{
return
jsonNode
.
has
(
field
)
?
jsonNode
.
get
(
field
)
:
MissingNode
.
getInstance
();
}
private
JsonNode
readJsonNode
(
JsonNode
jsonNode
,
String
field
)
{
return
jsonNode
.
has
(
field
)
?
jsonNode
.
get
(
field
)
:
MissingNode
.
getInstance
();
}
}
youlai-auth/src/main/java/com/youlai/auth/userdetails/user/jackson/SysUseMixin.java
→
youlai-auth/src/main/java/com/youlai/auth/userdetails/user/jackson/SysUse
r
Mixin.java
浏览文件 @
4a943ab5
...
...
@@ -6,14 +6,17 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
import
com.fasterxml.jackson.databind.annotation.JsonDeserialize
;
/**
* @see org.springframework.security.jackson2.UserMixin
* SysUserDetails 反序列化注册
*
* 刷新模式根据 refresh_token 从 oauth2_authorization 表中获取字段 attributes 内容反序列化成
*
* @author haoxr
* @see org.springframework.security.jackson2.UserMixin
* @since 2023/7/4
*/
@JsonTypeInfo
(
use
=
JsonTypeInfo
.
Id
.
CLASS
,
include
=
JsonTypeInfo
.
As
.
PROPERTY
)
@JsonDeserialize
(
using
=
SysUserDeserializer
.
class
)
@JsonAutoDetect
(
fieldVisibility
=
JsonAutoDetect
.
Visibility
.
ANY
,
getterVisibility
=
JsonAutoDetect
.
Visibility
.
NONE
,
isGetterVisibility
=
JsonAutoDetect
.
Visibility
.
NONE
)
@JsonIgnoreProperties
(
ignoreUnknown
=
true
)
public
class
SysUseMixin
{
public
class
SysUse
r
Mixin
{
}
youlai-auth/src/main/java/com/youlai/auth/util/RequestUtils.java
已删除
100644 → 0
浏览文件 @
a07a3c88
package
com.youlai.auth.util
;
import
cn.hutool.core.util.StrUtil
;
import
com.nimbusds.jose.JWSObject
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.web.context.request.RequestContextHolder
;
import
org.springframework.web.context.request.ServletRequestAttributes
;
import
jakarta.servlet.http.HttpServletRequest
;
import
java.nio.charset.StandardCharsets
;
import
java.text.ParseException
;
import
java.util.Base64
;
@Slf4j
public
class
RequestUtils
{
/**
* 获取登录认证的客户端ID
* <p>
* 兼容两种方式获取OAuth2客户端信息(client_id、client_secret)
* 方式一:client_id、client_secret放在请求路径中
* 方式二:放在请求头(Request Headers)中的Authorization字段,且经过加密,例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret
*
* @return
*/
public
static
String
getClientId
()
{
HttpServletRequest
request
=
((
ServletRequestAttributes
)
RequestContextHolder
.
getRequestAttributes
()).
getRequest
();
// 从请求路径中获取
String
clientId
=
request
.
getParameter
(
"client_id"
);
if
(
StrUtil
.
isNotBlank
(
clientId
))
{
return
clientId
;
}
// 从请求头获取
String
basic
=
request
.
getHeader
(
"Authorization"
);
if
(
StrUtil
.
isNotBlank
(
basic
)
&&
basic
.
startsWith
(
"Basic "
))
{
basic
=
basic
.
replace
(
"Basic "
,
""
);
String
basicPlainText
=
new
String
(
Base64
.
getDecoder
().
decode
(
basic
.
getBytes
(
StandardCharsets
.
UTF_8
)),
StandardCharsets
.
UTF_8
);
clientId
=
basicPlainText
.
split
(
":"
)[
0
];
//client:secret
}
return
clientId
;
}
/**
* 获取JWT Payload
*
* @return
*/
public
static
String
getJwtPayload
()
{
HttpServletRequest
request
=
((
ServletRequestAttributes
)
RequestContextHolder
.
getRequestAttributes
()).
getRequest
();
String
payload
=
null
;
String
authorization
=
request
.
getHeader
(
"Authorization"
);
if
(
StrUtil
.
isNotBlank
(
authorization
)
&&
StrUtil
.
startWithIgnoreCase
(
authorization
,
"Bearer "
))
{
authorization
=
StrUtil
.
replaceIgnoreCase
(
authorization
,
"Bearer "
,
""
);
try
{
payload
=
JWSObject
.
parse
(
authorization
).
getPayload
().
toString
();
}
catch
(
ParseException
e
)
{
log
.
error
(
e
.
getMessage
());
}
}
return
payload
;
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录