Skip to content

Commit e6023b6

Browse files
committed
feat(security): PermissionCode 修改
1 parent 049828d commit e6023b6

14 files changed

Lines changed: 296 additions & 53 deletions

File tree

admin4j-common-spring-web/src/main/java/com/admin4j/common/pojo/AuthenticationUser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import lombok.Data;
66

77
import java.io.Serializable;
8-
import java.util.Set;
8+
import java.util.Collection;
99

1010
/**
1111
* UserContext 用户上下文
@@ -35,7 +35,7 @@ public class AuthenticationUser implements Serializable {
3535
* 权限列表
3636
*/
3737
@ApiModelProperty("权限code列表")
38-
private Set<String> permissions;
38+
private Collection<String> permissions;
3939

4040
// private String fromService;
4141

admin4j-common/src/main/java/com/admin4j/common/pojo/ResponseEnum.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public enum ResponseEnum implements IResponse, Assert {
3333
*/
3434
FAIL_AUTH(402, "登录失败,账号或者密码错误"),
3535
/**
36-
*
36+
* 没有权限
3737
*/
3838
FAIL_AUTH_FORBIDDEN(403, "FAIL_AUTH_FORBIDDEN"),
3939

admin4j-dependencies/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.admin4j</groupId>
88
<artifactId>admin4j-dependencies</artifactId>
9-
<version>0.9.3-SNAPSHOT</version>
9+
<version>0.9.5-SNAPSHOT</version>
1010
<packaging>pom</packaging>
1111

1212
<name>${project.artifactId}</name>
@@ -64,7 +64,7 @@
6464

6565
<flowable.version>6.7.2</flowable.version>
6666
<admin4j-plugin.version>0.1.2</admin4j-plugin.version>
67-
<admin4j.version>0.9.0</admin4j.version>
67+
<admin4j.version>0.9.5-SNAPSHOT</admin4j.version>
6868
<admin4j-lock.version>0.8.2</admin4j-lock.version>
6969
<admin4j-limiter.version>0.8.0</admin4j-limiter.version>
7070
<admin4j-redis.version>0.8.0</admin4j-redis.version>

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<module>enum-spring-boot-starter</module>
4444
</modules>
4545
<properties>
46-
<revision>0.9.3-SNAPSHOT</revision>
46+
<revision>0.9.5-SNAPSHOT</revision>
4747
<admin4j-dependencies.version>0.9.3-SNAPSHOT</admin4j-dependencies.version>
4848
<maven.compiler.source>8</maven.compiler.source>
4949
<maven.compiler.target>8</maven.compiler.target>

security-spring-boot-starter/README.md

Lines changed: 170 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
# admin security
22

3-
## Features
3+
Spring Security 最佳实践封装。
4+
5+
# Features
46

57
- 多渠道登录
6-
- 一个注解/一个配置,解决匿名url访问(忽略认证)
8+
- 匿名url访问:一个注解/一个配置,解决匿名url访问(忽略认证)
9+
- 注解式权限
710
- 基于数据库的动态权限
811

9-
## USAGES
12+
# USAGES
13+
14+
## 1. 基础使用
1015

11-
1. 引入 pom
16+
### 1.1 引入 pom
1217

1318
```
1419
<dependency>
@@ -18,7 +23,7 @@
1823
</dependency>
1924
```
2025

21-
2. 实现 JwtUserDetailsService 接口,用于根据用户ID获取用户详情。由于我们的JWT Token 存的是 userId,所以这里的入参为userId
26+
### 1.2 实现 JwtUserDetailsService 接口,用于根据用户ID获取用户详情。由于我们的JWT Token 存的是 userId,所以这里的入参为userId
2227

2328
```java
2429

@@ -36,7 +41,7 @@ public class Admin4jJwtUserDetailsService implements JwtUserDetailsService {
3641
}
3742
```
3843

39-
3. 账号密码登录。需要实现 `Spring Security``UserDetailsService` 接口,用于根据 username 查询用户详情
44+
### 1.3 账号密码登录。需要实现 `Spring Security``UserDetailsService` 接口,用于根据 username 查询用户详情
4045

4146
```java
4247

@@ -63,7 +68,7 @@ public class Admin4jJwtUserDetailsService implements JwtUserDetailsService, User
6368

6469
```
6570

66-
### 测试
71+
### 1.4 测试
6772

6873
- 登录接口
6974

@@ -86,11 +91,166 @@ curl --location 'http://localhost:8080/login' \
8691
}
8792
```
8893

89-
## 多渠道登录
94+
## 2. 匿名url访问
95+
96+
### 2.1 注解式
97+
98+
> `@AnonymousAccess`需要放在 `controller`方法上
99+
100+
```
101+
public class UserProfileController {
102+
103+
@GetMapping("1")
104+
@ApiModelProperty("注解式匿名url访问")
105+
@AnonymousAccess
106+
public R<String> get() {
107+
return R.ok("1");
108+
}
109+
```
110+
111+
### 2.2 yml配置式
112+
113+
> 支持HttpMethod(get,post,put,delete)配置;uris下面表示所有HttpMethod
114+
115+
```
116+
admin4j:
117+
security:
118+
ignoring:
119+
uris:
120+
- "/login/sendPhoneCode"
121+
- "/profile/**"
122+
get:
123+
- "/profile/3"
124+
```
125+
126+
## 3. 注解式权限
127+
128+
### 3.1 开启方法注解式权限
129+
130+
```
131+
@EnableGlobalMethodSecurity(prePostEnabled = true)
132+
public class AdminServerApplication {
133+
}
134+
```
135+
136+
### 3.2 使用
137+
138+
```
139+
@GetMapping("4")
140+
@PreAuthorize("hasAuthority('profile')")
141+
public R<SysUserLoginInfoVO> get4() {
142+
return R.ok("4");
143+
}
144+
145+
@GetMapping("5")
146+
@PreAuthorize("hasAuthority('menus')")
147+
public R<String> get5() {
148+
return R.ok("5");
149+
}
150+
```
151+
152+
### 3.3 `@PreAuthorize` 支持el表达式
153+
154+
#### 3.3.1. returnObject 保留名
155+
156+
对于 @PostAuthorize@PostFilter 注解, 可以在表达式中使用 returnObject 保留名, returnObject 代表着被注解方法的返回值,
157+
我们可以使用 returnObject 保留名对注解方法的结果进行验证.
158+
比如:
159+
160+
```java
161+
162+
@PostAuthorize("returnObject.owner == authentication.name")
163+
public Book getBook();
164+
12
165+
```
166+
167+
#### 3.3.2. 表达式中的 # 号
168+
169+
在表达式中, 可以使用 #argument123 的形式来代表注解方法中的参数 argument123.
170+
比如:
171+
172+
```java
173+
174+
@PreAuthorize("#book.owner == authentication.name")
175+
public void deleteBook(Book book);
176+
12
177+
```
178+
179+
还有一种 #argument123 的写法, 即使用 Spring Security @P注解来为方法参数起别名, 然后在 @PreAuthorize 等注解表达式中使用该别名.
180+
不推荐这种写法, 代码可读性较差.
181+
182+
```java
183+
184+
@PreAuthorize("#c.name == authentication.name")
185+
public void doSomething(@P("c") Contact contact);
186+
```
187+
188+
#### 3.3.3 内置表达式有:
189+
190+
| 表达式 | 备注 |
191+
|--------------------------------------------------------------------|----------------------------------------|
192+
| hasRole([role]) | 如果有当前角色, 则返回 true(会自动加上 ROLE_ 前缀) |
193+
| hasAnyRole([role1, role2]) | 如果有任一角色即可通过校验, 返回true,(会自动加上 ROLE_ 前缀) |
194+
| hasAuthority([authority]) | 如果有指定权限, 则返回 true |
195+
| hasAnyAuthority([authority1, authority2]) | 如果有任一指定权限, 则返回true |
196+
| principal | 获取当前用户的 principal 主体对象 |
197+
| authentication | 获取当前用户的 authentication 对象, |
198+
| permitAll | 总是返回 true, 表示全部允许 |
199+
| denyAll | 总是返回 false, 代表全部拒绝 |
200+
| isAnonymous() | 如果是匿名访问, 返回true |
201+
| isRememberMe() | 如果是remember-me 自动认证, 则返回 true |
202+
| isAuthenticated() | 如果不是匿名访问, 则返回true |
203+
| isFullAuthenticated() | 如果不是匿名访问或remember-me认证登陆, 则返回true |
204+
| hasPermission(Object target, Object permission) | |
205+
| hasPermission(Object target, String targetType, Object permission) | |
206+
207+
## 4. 基于数据库的动态权限
208+
209+
实现 接口`IPermissionUrlService`
210+
211+
```
212+
public interface IPermissionUrlService {
213+
214+
/**
215+
* 是否忽略 检查权限
216+
* 例如 admin、管理员可以直接忽略检查拥有全部权限
217+
*
218+
* @return
219+
*/
220+
default boolean ignoreCheck() {
221+
return false;
222+
}
223+
224+
/**
225+
* 是否允许匿名访问
226+
*
227+
* @return
228+
*/
229+
default boolean canAnonymousAccess() {
230+
return false;
231+
}
232+
233+
/**
234+
* 获取系统所有需要授权的 PermissionUri
235+
*
236+
* @return
237+
*/
238+
List<HttpUrlPermission> allPermissionUrl();
239+
240+
/**
241+
* 当前用户拥有的权限
242+
*
243+
* @return
244+
*/
245+
List<HttpUrlPermission> getMyPermissionUrls();
246+
}
247+
```
248+
249+
## 5.多渠道登录
90250

91251
通过配置的方式,支持微信,手机号等多渠道登录
92252

93-
### 验证码手机号登录
253+
### 5.1 验证码手机号登录
94254

95255
- yaml配置方式
96256

@@ -127,7 +287,6 @@ curl --location 'http://localhost:8080/login/phone' \
127287
3. 根据手机号获取用户详情
128288

129289
```java
130-
131290
/**
132291
* 验证码手机号登录
133292
*
@@ -162,3 +321,4 @@ public class PhoneMultiUserDetailsService implements MultiUserDetailsService {
162321
```
163322

164323
### 其他渠道登录,如微信openid 登录,参考上方可实现
324+

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,18 @@ public interface IPermissionUrlService {
1919
default boolean ignoreCheck() {
2020
return false;
2121
}
22-
22+
23+
/**
24+
* 是否允许匿名访问
25+
*
26+
* @return
27+
*/
28+
default boolean canAnonymousAccess() {
29+
return false;
30+
}
31+
2332
/**
24-
* 获取 系统 所有的 PermissionUri
33+
* 获取系统所有需要授权的 PermissionUri
2534
*
2635
* @return
2736
*/

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

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

33
import lombok.RequiredArgsConstructor;
4+
import org.springframework.security.authentication.AnonymousAuthenticationToken;
45
import org.springframework.security.authorization.AuthorizationDecision;
56
import org.springframework.security.authorization.AuthorizationManager;
67
import org.springframework.security.core.Authentication;
@@ -43,6 +44,15 @@ public class PermissionAuthorizationManager implements AuthorizationManager<Requ
4344
@Override
4445
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
4546

47+
// 是否允许匿名访问
48+
if (!canAnonymousAccess()) {
49+
Authentication authenticationGet = authentication.get();
50+
if (authenticationGet instanceof AnonymousAuthenticationToken) {
51+
// 匿名访问
52+
return UN_AUTHORIZED;
53+
}
54+
}
55+
4656
if (ignoreCheck()) {
4757
return GRANTED;
4858
}
@@ -59,6 +69,16 @@ public AuthorizationDecision check(Supplier<Authentication> authentication, Requ
5969
return urlNeedPermission(requestURI, method) ? UN_AUTHORIZED : GRANTED;
6070
}
6171

72+
/**
73+
* 是否允许匿名访问。
74+
* 默认不允许。
75+
*
76+
* @return
77+
*/
78+
protected boolean canAnonymousAccess() {
79+
return permissionUriService.canAnonymousAccess();
80+
}
81+
6282
/**
6383
* 是否忽略 检查权限
6484
* 例如 admin、管理员可以直接忽略检查拥有全部权限
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.admin4j.framework.security.authorization;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Data;
5+
import lombok.NoArgsConstructor;
6+
import org.springframework.security.access.AccessDecisionManager;
7+
import org.springframework.security.core.GrantedAuthority;
8+
9+
/**
10+
* 权限字符串code
11+
*
12+
* @author andanyang
13+
* @since 2023/12/20 10:29
14+
*/
15+
@AllArgsConstructor
16+
@NoArgsConstructor
17+
@Data
18+
public class PermissionGrantedAuthority implements GrantedAuthority {
19+
20+
private static final long serialVersionUID = -2619854289135953331L;
21+
// 权限字符串code
22+
private String permission;
23+
24+
/**
25+
* If the <code>GrantedAuthority</code> can be represented as a <code>String</code>
26+
* and that <code>String</code> is sufficient in precision to be relied upon for an
27+
* access control decision by an {@link AccessDecisionManager} (or delegate), this
28+
* method should return such a <code>String</code>.
29+
* <p>
30+
* If the <code>GrantedAuthority</code> cannot be expressed with sufficient precision
31+
* as a <code>String</code>, <code>null</code> should be returned. Returning
32+
* <code>null</code> will require an <code>AccessDecisionManager</code> (or delegate)
33+
* to specifically support the <code>GrantedAuthority</code> implementation, so
34+
* returning <code>null</code> should be avoided unless actually required.
35+
*
36+
* @return a representation of the granted authority (or <code>null</code> if the
37+
* granted authority cannot be expressed as a <code>String</code> with sufficient
38+
* precision).
39+
*/
40+
@Override
41+
public String getAuthority() {
42+
return permission;
43+
}
44+
}

0 commit comments

Comments
 (0)