Skip to content

Commit c397d5d

Browse files
committed
admin security 添加文档
1 parent dbd69aa commit c397d5d

12 files changed

Lines changed: 196 additions & 43 deletions
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# admin security
2+
3+
## USAGES
4+
5+
1. 引入 pom
6+
7+
```
8+
<dependency>
9+
<groupId>com.admin4j.framework</groupId>
10+
<artifactId>security-spring-boot-starter</artifactId>
11+
<version>0.9.0</version>
12+
</dependency>
13+
```
14+
15+
2. 实现 JwtUserDetailsService 接口,用于根据用户ID获取用户详情。由于我们的JWT Token 存的是 userId,所以这里的入参为userId
16+
17+
```java
18+
19+
@Component
20+
public class Admin4jJwtUserDetailsService implements JwtUserDetailsService {
21+
22+
@Autowired
23+
ISysUserLongInfoService sysUserLongInfoService;
24+
25+
@Override
26+
public JwtUserDetails loadUserByUserId(Long userId) {
27+
28+
return sysUserLongInfoService.getByUserId(userId);
29+
}
30+
}
31+
```
32+
33+
3. 账号密码登录。需要实现 `Spring Security``UserDetailsService` 接口,用于根据 username 查询用户详情
34+
35+
```java
36+
37+
@Component
38+
public class Admin4jJwtUserDetailsService implements JwtUserDetailsService, UserDetailsService {
39+
40+
@Autowired
41+
ISysUserLongInfoService sysUserLongInfoService;
42+
@Autowired
43+
ISysUserService sysUserService;
44+
45+
@Override
46+
public JwtUserDetails loadUserByUserId(Long userId) {
47+
48+
return sysUserLongInfoService.getByUserId(userId);
49+
}
50+
51+
@Override
52+
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
53+
SysUser sysUser = sysUserService.getByUserName(username);
54+
return SysUserConvert.INSTANCE.convert(sysUser);
55+
}
56+
}
57+
58+
```
59+
60+
### 测试
61+
62+
- 登录接口
63+
64+
```curl
65+
curl --location 'http://localhost:8080/login' \
66+
--header 'Content-Type: application/x-www-form-urlencoded' \
67+
--data-urlencode 'username=admin' \
68+
--data-urlencode 'password=123456'
69+
```
70+
71+
- 返回结果
72+
73+
```json
74+
{
75+
"code": 200,
76+
"data": {
77+
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzYWx0IjoiMTIiLCJleHAiOjE2OTkzNDU3ODAsInVzZXJJRCI6MX0.tz0RGKSQEwfS0aTrsF7bdxF1enU4Vy32rn4ckDn3-D0"
78+
},
79+
"msg": "success"
80+
}
81+
```
82+
83+
## 多渠道登录
84+
85+
通过配置的方式,支持微信,手机号等多渠道登录
86+
87+
### 验证码手机号登录
88+
89+
- yaml配置方式
90+
91+
```yaml
92+
admin4j:
93+
security:
94+
multi:
95+
auth-map:
96+
phone: phoneNumber
97+
98+
```
99+
100+
auth-map 为渠道登录登录方式配置。对象类型,key为 登录方式 (`authType`)。value 为登录认证的字段名称。
101+
解释如上配置,
102+
103+
- 登录方式 (`authType`) : `phone`
104+
- 登录认证的字段名称: `phoneNumber`
105+
106+
前端登录接口案例:`/login/phone` (/login/authType)
107+
108+
```curl
109+
curl --location 'http://localhost:8080/login/phone' \
110+
--header 'Content-Type: application/x-www-form-urlencoded' \
111+
--data-urlencode 'phoneNumber=admin' \
112+
--data-urlencode 'verificationCode=123456'
113+
```
114+
115+
- java 配置代码,实现接口`MultiUserDetailsService`
116+
1. 配置登录方式 `support()` 为 phone (yaml配置的`authType`
117+
2. 检查手机对应的验证码是否正确
118+
3. 根据手机号获取用户详情
119+
120+
```java
121+
122+
/**
123+
* 验证码手机号登录
124+
*
125+
*/
126+
@Component
127+
public class PhoneMultiUserDetailsService implements MultiUserDetailsService {
128+
@Autowired
129+
ISysUserService sysUserService;
130+
131+
@Override
132+
public String support() {
133+
return "phone";
134+
}
135+
136+
@Override
137+
public boolean preVerify(MultiAuthenticationToken multiAuthenticationToken) {
138+
139+
// 验证码
140+
String verificationCode = multiAuthenticationToken.getAuthParameter("verificationCode");
141+
// TODO 验证验证码正确性。错误返回 false,或者抛出错误
142+
return true;
143+
}
144+
145+
@Override
146+
public UserDetails loadUserByMultiToken(String phoneNumber) {
147+
148+
SysUser sysUser = sysUserService.getByPhoneNumber(phoneNumber);
149+
return SysUserConvert.INSTANCE.convert(sysUser);
150+
}
151+
}
152+
153+
```
154+
155+
### 其他渠道登录,如微信openid 登录,参考上方可实现

security-spring-boot-starter/pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
22
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
33
<modelVersion>4.0.0</modelVersion>
44
<parent>
@@ -10,6 +10,7 @@
1010
<groupId>com.admin4j.framework</groupId>
1111
<artifactId>security-spring-boot-starter</artifactId>
1212
<packaging>jar</packaging>
13+
<version>0.9.1-SNAPSHOT</version>
1314

1415
<name>security-spring-boot-starter</name>
1516

security-spring-boot-starter/src/main/java/com/admin4j/framework/security/UserTokenService.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.admin4j.framework.security;
22

33
import com.admin4j.framework.security.jwt.JwtUserDetails;
4-
import org.springframework.security.core.userdetails.UserDetails;
54

65
import javax.servlet.http.HttpServletRequest;
76

@@ -39,5 +38,5 @@ public interface UserTokenService {
3938
* @param token
4039
* @return 登录用户名
4140
*/
42-
UserDetails getUserDetails(String token);
41+
JwtUserDetails getUserDetails(String token);
4342
}

security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public AuthenticationManager authenticationManager(AuthenticationConfiguration a
100100
* 取消ROLE_前缀
101101
*/
102102
//@Bean
103-
//public GrantedAuthorityDefaults grantedAuthorityDefaults() {
103+
// public GrantedAuthorityDefaults grantedAuthorityDefaults() {
104104
// // Remove the ROLE_ prefix
105105
// return new GrantedAuthorityDefaults("");
106106
//}
@@ -122,9 +122,9 @@ public ReloadableResourceBundleMessageSource messageSource() {
122122
@ConditionalOnMissingBean(SecurityFilterChain.class)
123123
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
124124

125-
//FilterSecurityInterceptor
125+
// FilterSecurityInterceptor
126126
httpSecurity
127-
//关闭cors
127+
// 关闭cors
128128
.cors().disable()
129129
// CSRF禁用,因为不使用session
130130
.csrf().disable()
@@ -161,7 +161,7 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti
161161

162162
authorizeRequestsConfigurer(httpSecurity);
163163

164-
//多渠道登录
164+
// 多渠道登录
165165
if (multiAuthenticationProperties.isEnable()) {
166166

167167
MultiAuthenticationFilter authenticationFilter = new MultiAuthenticationFilter(multiAuthenticationProperties, formLoginProperties);
@@ -174,7 +174,7 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti
174174
httpSecurity.apply(multiSecurityConfigurerAdapter);
175175

176176
} else {
177-
//开启form表单认证
177+
// 开启form表单认证
178178
httpSecurity.formLogin()
179179
.loginProcessingUrl(formLoginProperties.getLoginProcessingUrl())
180180
.passwordParameter(formLoginProperties.getPasswordParameter())
@@ -189,8 +189,8 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti
189189
httpSecurity.addFilterBefore(actuatorFilter, UsernamePasswordAuthenticationFilter.class);
190190
}
191191

192-
//GlobalAuthenticationConfigurerAdapter
193-
//WebSecurityConfigurerAdapter
192+
// GlobalAuthenticationConfigurerAdapter
193+
// WebSecurityConfigurerAdapter
194194
return httpSecurity.build();
195195
}
196196

@@ -200,7 +200,7 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti
200200
private void authorizeRequestsConfigurer(HttpSecurity httpSecurity) throws Exception {
201201
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry =
202202
httpSecurity.authorizeRequests();
203-
//忽略URl配置
203+
// 忽略URl配置
204204
ignoringRequestMatcherRegistry(expressionInterceptUrlRegistry);
205205
// 除上面外的所有请求全部需要鉴权认证;其他路径必须验证
206206
expressionInterceptUrlRegistry.anyRequest().authenticated();
@@ -249,7 +249,7 @@ private void ignoringRequestMatcherRegistry(ExpressionUrlAuthorizationConfigurer
249249
}
250250
}
251251

252-
//AnonymousAccess 注解
252+
// AnonymousAccess 注解
253253
if (anonymousAccessUrl != null) {
254254

255255
Map<HttpMethod, String[]> anonymousUrl = anonymousAccessUrl.getAnonymousUrl();
@@ -265,7 +265,7 @@ private void ignoringRequestMatcherRegistry(ExpressionUrlAuthorizationConfigurer
265265
*/
266266
//@Bean
267267
//@ConditionalOnMissingBean(WebSecurityCustomizer.class)
268-
//public WebSecurityCustomizer webSecurityCustomizer() {
268+
// public WebSecurityCustomizer webSecurityCustomizer() {
269269
// return (web) -> {
270270
//
271271
// //设置匿名访问url

security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityHandlerConfiguration.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,10 @@
1212
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
1313
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
1414

15-
/**
16-
* @author andanyang
17-
* @since 2023/5/31 18:06
18-
*/
15+
1916
public class SecurityHandlerConfiguration {
2017
@Bean
21-
@ConditionalOnMissingBean(AuthenticationResult.class)
18+
@ConditionalOnMissingBean({AuthenticationResult.class})
2219
public AuthenticationResult authenticationResult(UserTokenService userTokenService) {
2320
return new DefaultAuthenticationResult(userTokenService);
2421
}

security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.admin4j.framework.security.properties.ActuatorProperties;
1111
import com.admin4j.framework.security.properties.FormLoginProperties;
1212
import com.admin4j.framework.security.properties.JwtProperties;
13+
import org.springframework.beans.factory.annotation.Autowired;
1314
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
1415
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
1516
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -28,8 +29,8 @@
2829
public class UserTokenServiceConfiguration {
2930

3031
@Bean
31-
@ConditionalOnMissingBean(UserTokenService.class)
32-
public UserTokenService userTokenService(JwtProperties jwtProperties, JwtUserDetailsService jwtUserDetailsService) {
32+
@ConditionalOnMissingBean({UserTokenService.class})
33+
public UserTokenService userTokenService(JwtProperties jwtProperties, @Autowired(required = false) JwtUserDetailsService jwtUserDetailsService) {
3334
return new JwtUserTokenService(jwtProperties, jwtUserDetailsService);
3435
}
3536

security-spring-boot-starter/src/main/java/com/admin4j/framework/security/filter/JwtAuthenticationTokenFilter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import org.springframework.beans.factory.annotation.Autowired;
1212
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1313
import org.springframework.security.core.context.SecurityContextHolder;
14-
import org.springframework.security.core.userdetails.UserDetails;
1514
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
1615
import org.springframework.web.filter.OncePerRequestFilter;
1716

@@ -47,7 +46,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
4746
}
4847

4948
try {
50-
UserDetails userDetails = userTokenService.getUserDetails(token);
49+
JwtUserDetails userDetails = userTokenService.getUserDetails(token);
5150

5251
if (userDetails != null && SecurityContextHolder.getContext().getAuthentication() == null) {
5352
// 认证成功吧 Authentication 对象 setAuthenticated(true), 然存到 SecurityContext 中

security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/DefaultAuthenticationResult.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@
3333
public class DefaultAuthenticationResult implements AuthenticationResult {
3434

3535

36-
protected final UserTokenService userTokenService;
3736
protected static final IResponse FAIL_AUTH_FORBIDDEN = new SimpleResponse(ResponseEnum.FAIL_AUTH_FORBIDDEN);
3837
protected static final IResponse FAIL_AUTH = new SimpleResponse(ResponseEnum.FAIL_AUTH_TOKEN);
38+
protected final UserTokenService userTokenService;
3939

4040
/**
4141
* 认证成功回调
@@ -54,12 +54,14 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
5454

5555
SpringUtils.getApplicationContext().publishEvent(new AuthenticationSuccessEvent(request, response, authentication));
5656

57-
String token = userTokenService.createToken((JwtUserDetails) authentication.getPrincipal());
58-
5957
SimpleResponse simpleResponse = new SimpleResponse(ResponseEnum.SUCCESS);
6058
Map<String, Object> map = new HashMap<>();
61-
map.put("token", token);
6259
simpleResponse.setData(map);
60+
61+
if (userTokenService != null) {
62+
String token = userTokenService.createToken((JwtUserDetails) authentication.getPrincipal());
63+
map.put("token", token);
64+
}
6365
ServletUtils.renderString(response, JSON.toJSONString(simpleResponse));
6466
}
6567

security-spring-boot-starter/src/main/java/com/admin4j/framework/security/jwt/JwtUserDetailsService.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.admin4j.framework.security.jwt;
22

3-
import org.springframework.security.core.userdetails.UserDetails;
4-
53
/**
4+
* 根据用户ID,获取用户信息
5+
*
66
* @author andanyang
77
* @since 2023/6/3 22:04
88
*/
@@ -19,7 +19,7 @@ public interface JwtUserDetailsService {
1919
* @param salt
2020
* @return
2121
*/
22-
default boolean verifySalt(UserDetails userDetails, String salt) {
22+
default boolean verifySalt(JwtUserDetails userDetails, String salt) {
2323
return true;
2424
}
2525

@@ -29,7 +29,7 @@ default boolean verifySalt(UserDetails userDetails, String salt) {
2929
* @param userId
3030
* @return
3131
*/
32-
UserDetails loadUserByUserId(Long userId);
32+
JwtUserDetails loadUserByUserId(Long userId);
3333

3434

3535
}

0 commit comments

Comments
 (0)