Skip to content

Commit 47ebcbc

Browse files
committed
优化权限
1 parent 27383bb commit 47ebcbc

File tree

7 files changed

+196
-24
lines changed

7 files changed

+196
-24
lines changed

hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/Authorize.java

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,22 +75,6 @@
7575
*/
7676
String message() default "{unauthorized}";
7777

78-
/**
79-
* 表达式验证
80-
*
81-
* @return 表达式
82-
* @see RequiresExpression#value()
83-
*/
84-
String expression() default "";
85-
86-
/**
87-
* 表达式语言,默认spring表达式
88-
*
89-
* @return 表达式语言
90-
* @see RequiresExpression#language()
91-
*/
92-
String expressionLanguage() default "spel";
93-
9478
/**
9579
* 是否合并类上的注解
9680
*
@@ -103,6 +87,6 @@
10387
*
10488
* @return logical
10589
*/
106-
Logical logical() default Logical.OR;
90+
Logical logical() default Logical.DEFAULT;
10791

10892
}

hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/Logical.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@
1818
package org.hswebframework.web.authorization.annotation;
1919

2020
public enum Logical {
21-
AND,OR
21+
AND, OR, DEFAULT
2222
}

hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/ListenerAuthorizingRealm.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,9 @@ public void onAuthorizeSuccess(boolean isRemembered, Authorization authorization
7676
.stream()
7777
.map(permission -> {
7878
StringBuilder builder = new StringBuilder(permission.getId());
79-
builder.append(":*")
80-
.append(permission.getActions().isEmpty() ? "" : ",")
81-
.append(permission.getActions().stream()
82-
.reduce((a1, a2) -> a1.concat(",").concat(a2)));
79+
builder.append(permission.getActions().stream()
80+
.reduce((a1, a2) -> a1.concat(",").concat(a2))
81+
.orElse(""));
8382
return new WildcardPermission(builder.toString());
8483
}).collect(Collectors.toList()));
8584

hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/ShiroAutoconfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ ResponseMessage handleException(AuthorizationException exception) {
209209
@ResponseStatus(HttpStatus.UNAUTHORIZED)
210210
@ResponseBody
211211
ResponseMessage handleException(UnauthenticatedException exception) {
212-
return ResponseMessage.error("{access_denied}", 401);
212+
return ResponseMessage.error(exception.getMessage() == null ? "{access_denied}" : exception.getMessage(), 401);
213213
}
214214
}
215215

hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/BoostAuthorizationAttributeSourceAdvisor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,14 @@ public class BoostAuthorizationAttributeSourceAdvisor extends StaticMethodMatche
6363
public BoostAuthorizationAttributeSourceAdvisor(DataAccessController dataAccessController,
6464
FieldAccessController fieldAccessController) {
6565
AopAllianceAnnotationsAuthorizingMethodInterceptor interceptor = new AopAllianceAnnotationsAuthorizingMethodInterceptor();
66+
// @RequiresExpression support
6667
interceptor.getMethodInterceptors().add(new ExpressionAnnotationMethodInterceptor());
68+
// @RequiresDataAccess support
6769
interceptor.getMethodInterceptors().add(new DataAccessAnnotationMethodInterceptor(dataAccessController));
70+
// @RequiresFieldAccess support
6871
interceptor.getMethodInterceptors().add(new FieldAccessAnnotationMethodInterceptor(fieldAccessController));
72+
// @Authorize support
73+
interceptor.getMethodInterceptors().add(new SimpleAuthorizeMethodInterceptor());
6974
setAdvice(interceptor);
7075
}
7176

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Copyright 2016 http://www.hswebframework.org
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*
17+
*/
18+
19+
package org.hswebframework.web.authorization.shiro.boost;
20+
21+
import org.apache.commons.codec.digest.DigestUtils;
22+
import org.apache.shiro.authz.AuthorizationException;
23+
import org.apache.shiro.authz.UnauthenticatedException;
24+
import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
25+
import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
26+
import org.hswebframework.expands.script.engine.DynamicScriptEngine;
27+
import org.hswebframework.expands.script.engine.DynamicScriptEngineFactory;
28+
import org.hswebframework.web.authorization.Authorization;
29+
import org.hswebframework.web.authorization.AuthorizationHolder;
30+
import org.hswebframework.web.authorization.Permission;
31+
import org.hswebframework.web.authorization.Role;
32+
import org.hswebframework.web.authorization.annotation.Authorize;
33+
import org.hswebframework.web.authorization.annotation.Logical;
34+
import org.hswebframwork.utils.ClassUtils;
35+
import org.hswebframwork.utils.StringUtils;
36+
import org.slf4j.Logger;
37+
import org.slf4j.LoggerFactory;
38+
39+
import java.lang.annotation.Annotation;
40+
import java.util.*;
41+
import java.util.function.Function;
42+
import java.util.function.Predicate;
43+
import java.util.stream.Collectors;
44+
45+
/**
46+
* 对{@link Authorize} 注解的支持
47+
*
48+
* @author zhouhao
49+
*/
50+
public class SimpleAuthorizeMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {
51+
public SimpleAuthorizeMethodInterceptor() {
52+
super(new AuthorizeAnnotationHandler());
53+
}
54+
55+
private static final Logger logger = LoggerFactory.getLogger(SimpleAuthorizeMethodInterceptor.class);
56+
57+
static class AuthorizeAnnotationHandler extends AuthorizingAnnotationHandler {
58+
59+
public AuthorizeAnnotationHandler() {
60+
super(Authorize.class);
61+
}
62+
63+
@Override
64+
public void assertAuthorized(Annotation a) throws AuthorizationException {
65+
if (!(a instanceof Authorize)) return;
66+
MethodInterceptorHolder holder = MethodInterceptorHolder.current();
67+
if (null == holder) {
68+
return;
69+
}
70+
AuthorizeConfig authorizeConfig = new AuthorizeConfig(holder.getArgs());
71+
Authorize authorize = ((Authorize) a);
72+
if (authorize.merge()) {
73+
Authorize classAnn = ClassUtils.getAnnotation(holder.getTarget().getClass(), Authorize.class);
74+
if (null != classAnn) {
75+
authorizeConfig.put(classAnn);
76+
}
77+
}
78+
authorizeConfig.put(authorize);
79+
80+
Authorization authorization = AuthorizationHolder.get();
81+
if (null == authorization) throw new UnauthenticatedException(authorizeConfig.message);
82+
boolean access = true;
83+
Logical logical = authorizeConfig.logical == Logical.DEFAULT ? Logical.OR : authorizeConfig.logical;
84+
boolean logicalIsOr = logical == Logical.OR;
85+
// 控制权限
86+
if (!authorizeConfig.permission.isEmpty()) {
87+
List<Permission> permissions = authorization.getPermissions().stream()
88+
.filter(permission -> {
89+
// 未持有任何一个权限
90+
if (!authorizeConfig.permission.contains(permission.getId())) return false;
91+
//未配置action
92+
if (authorizeConfig.action.isEmpty())
93+
return true;
94+
//判断action
95+
List<String> actions = permission.getActions()
96+
.stream()
97+
.filter(authorizeConfig.action::contains)
98+
.collect(Collectors.toList());
99+
//如果 控制逻辑是or,则只要过滤结果数量不为0.否则过滤结果数量必须和配置的数量相同
100+
return logicalIsOr ? actions.size() > 0 : actions.size() == permission.getActions().size();
101+
}).collect(Collectors.toList());
102+
access = logicalIsOr ? permissions.size() > 0 : permissions.size() == authorizeConfig.permission.size();
103+
}
104+
//控制角色
105+
if (!authorizeConfig.role.isEmpty()) {
106+
Function<Predicate<Role>, Boolean> func = logicalIsOr
107+
? authorization.getRoles().stream()::anyMatch
108+
: authorization.getRoles().stream()::allMatch;
109+
access = func.apply(role -> authorizeConfig.role.contains(role.getId()));
110+
}
111+
//控制用户
112+
if (!authorizeConfig.user.isEmpty()) {
113+
Function<Predicate<String>, Boolean> func = logicalIsOr
114+
? authorizeConfig.user.stream()::anyMatch
115+
: authorizeConfig.user.stream()::allMatch;
116+
access = func.apply(authorization.getUser().getId()::equals);
117+
}
118+
if (!access) {
119+
throw new AuthorizationException(authorizeConfig.message);
120+
}
121+
}
122+
}
123+
124+
static class AuthorizeConfig {
125+
Set<String> permission = new LinkedHashSet<>();
126+
Set<String> action = new LinkedHashSet<>();
127+
Set<String> role = new LinkedHashSet<>();
128+
Set<String> user = new LinkedHashSet<>();
129+
Logical logical = Logical.DEFAULT;
130+
String message = "unauthorized";
131+
Map<String, Object> var = null;
132+
133+
public AuthorizeConfig(Map<String, Object> var) {
134+
this.var = var;
135+
}
136+
137+
public void put(Authorize authorize) {
138+
permission.addAll(tryCompileExpression(authorize.permission()));
139+
action.addAll(tryCompileExpression(authorize.action()));
140+
role.addAll(tryCompileExpression(authorize.role()));
141+
user.addAll(tryCompileExpression(authorize.user()));
142+
if (!StringUtils.isNullOrEmpty(authorize.message())) {
143+
message = tryCompileExpression(authorize.message());
144+
}
145+
if (authorize.logical() != Logical.DEFAULT)
146+
logical = authorize.logical();
147+
}
148+
149+
public String tryCompileExpression(String express) {
150+
if (express.startsWith("${") && express.endsWith("}")) {
151+
express = express.substring(2, express.length() - 1);
152+
DynamicScriptEngine spelEngine = DynamicScriptEngineFactory.getEngine("spel");
153+
String id = DigestUtils.md5Hex(express);
154+
try {
155+
if (!spelEngine.compiled(id))
156+
spelEngine.compile(id, express);
157+
return String.valueOf(spelEngine.execute(id, var).getIfSuccess());
158+
} catch (Exception e) {
159+
throw new AuthorizationException("系统错误", e);
160+
} finally {
161+
// spelEngine.remove(id);
162+
}
163+
} else {
164+
return express;
165+
}
166+
}
167+
168+
public Collection<String> tryCompileExpression(String... expresses) {
169+
return Arrays.stream(expresses)
170+
.filter(Objects::nonNull)
171+
.map(this::tryCompileExpression)
172+
.collect(Collectors.toSet());
173+
}
174+
}
175+
}

hsweb-examples/hsweb-examples-simple/src/main/java/org/hswebframework/web/example/simple/TestController.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package org.hswebframework.web.example.simple;
22

3+
import org.apache.shiro.authz.annotation.RequiresPermissions;
34
import org.apache.shiro.authz.annotation.RequiresUser;
45
import org.hswebframework.web.authorization.Authorization;
56
import org.hswebframework.web.authorization.AuthorizationHolder;
67
import org.hswebframework.web.authorization.Permission;
78
import org.hswebframework.web.authorization.annotation.AuthInfo;
9+
import org.hswebframework.web.authorization.annotation.Authorize;
810
import org.hswebframework.web.authorization.annotation.RequiresDataAccess;
911
import org.hswebframework.web.authorization.annotation.RequiresFieldAccess;
1012
import org.hswebframework.web.commons.entity.Entity;
@@ -27,10 +29,17 @@
2729
* @author zhouhao
2830
*/
2931
@RestController
32+
@Authorize(permission = "test")
3033
public class TestController implements QueryController<UserEntity, String, QueryParamEntity> {
3134

35+
@GetMapping("/test1")
36+
@Authorize(action = "query", message = "${'表达式方式'}")
37+
public ResponseMessage testSimple(@AuthInfo Authorization authorization) {
38+
return ResponseMessage.ok(authorization);
39+
}
40+
3241
@GetMapping("/test")
33-
@RequiresUser
42+
@RequiresPermissions("test:*")
3443
public ResponseMessage testShiro(@AuthInfo Authorization authorization) {
3544
return ResponseMessage.ok(authorization);
3645
}

0 commit comments

Comments
 (0)