spring-shiro权限控制realm
用户与角色实体
Role.java
1
2
3
4
5
6
7
8
9
|
@Data @Entity public class Role { @Id @GeneratedValue private Integer id; private Long userId; private String role; } |
User.java
1
2
3
4
5
6
7
8
9
|
@Data @Entity public class User { @Id @GeneratedValue private Long id; private String username; private String password; } |
Realm类
首先建立 Realm 类,继承自 AuthorizingRealm,自定义我们自己的授权和认证的方法。Realm 是可以访问特定于应用程序的安全性数据(如用户,角色和权限)的组件。
Realm.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public class Realm extends AuthorizingRealm { @Autowired private UserService userService; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //从凭证中获得用户名 String username = (String) SecurityUtils.getSubject().getPrincipal(); //根据用户名查询用户对象 User user = userService.getUserByUserName(username); //查询用户拥有的角色 List<Role> list = roleService.findByUserId(user.getId()); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); for (Role role : list) { //赋予用户角色 info.addStringPermission(role.getRole()); } return info; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //获得当前用户的用户名 String username = (String) authenticationToken.getPrincipal(); //从数据库中根据用户名查找用户 User user = userService.getUserByUserName(username); if (userService.getUserByUserName(username) == null ) { throw new UnknownAccountException( "没有在本系统中找到对应的用户信息。" ); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),getName()); return info; } } |
Shiro 配置类
ShiroConfig.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); //以下是过滤链,按顺序过滤,所以/**需要放最后 //开放的静态资源 filterChainDefinitionMap.put( "/favicon.ico" , "anon" ); //网站图标 filterChainDefinitionMap.put( "/**" , "authc" ); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(myRealm()); return defaultWebSecurityManager; } @Bean public MyRealm myRealm() { MyRealm myRealm = new MyRealm(); return myRealm; } } |
控制器
UserController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
@Controller public class UserController { @Autowired private UserService userService; @GetMapping ( "/" ) public String index() { return "index" ; } @GetMapping ( "/login" ) public String toLogin() { return "login" ; } @GetMapping ( "/admin" ) public String admin() { return "admin" ; } @PostMapping ( "/login" ) public String doLogin(String username, String password) { UsernamePasswordToken token = new UsernamePasswordToken(username, password); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); } catch (Exception e) { e.printStackTrace(); } return "redirect:admin" ; } @GetMapping ( "/home" ) public String home() { Subject subject = SecurityUtils.getSubject(); try { subject.checkPermission( "admin" ); } catch (UnauthorizedException exception) { System.out.println( "没有足够的权限" ); } return "home" ; } @GetMapping ( "/logout" ) public String logout() { return "index" ; } } |
Service
UserService.java
1
2
3
4
5
6
7
8
9
10
11
12
|
@Service public class UserService { @Autowired private UserDao userDao; public User getUserByUserName(String username) { return userDao.findByUsername(username); } @RequiresRoles ( "admin" ) public void send() { System.out.println( "我现在拥有角色admin,可以执行本条语句" ); } } |
shiro权限不生效原因分析
shiro遇到的坑
-项目中使用shiro做登录校验和权限管理,在配置权限时遇到小坑,记录一下。
- 环境:springboot+freemarker+shiro
- 场景:后台管理,配置菜单以及按钮权限,分为三个层级,一二级暂时只考虑是否查看权限,第三层级为页面按钮权限,分增删改查。详情看图
- 问题:一二层级正常,第三层级权限不起作用!
权限标签定义如下:
标签定义 | 页面一 | 页面二 |
---|---|---|
第一层级 | one:view | two:view |
第二层级 | one:page1:view | two:page2:view |
第三层级 | one:page1:view:add | two:page2:view:add |
开始怀疑是数据库没有录入,查看后权限标签与角色已对应,排除。
后面怀疑是页面问题,后面把第三层级标签与第一二层级同一页面,依然不起作用,排除。
后面怀疑是权限标签定义问题,把第三层级标签改为one:page1:data:add,奇迹出现,权限生效。证实权限标签定义出了问题。
问题原因:权限标签定义问题
但是后来想想为什么会出现这种问题,每个标签都是独一无二的,对此我对shiro对于权限标签的校验产生了兴趣,查看源码,一路debug后最终在org.apache.shiro.authz.permission中看到了关键所在,核心代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
//当这个方法返回true时说明有此权限 //这个p是代表当前循环匹配到的权限标签 public boolean implies(Permission p) { // By default only supports comparisons with other WildcardPermissions if (!(p instanceof WildcardPermission)) { return false ; } WildcardPermission wp = (WildcardPermission) p; //把当前标签转分割成一个set集合(如one:page1:view:add 会分割成[[one], [page1], [view], [add]]) List<Set<String>> otherParts = wp.getParts(); int i = 0 ; //循环匹配权限标签 for (Set<String> otherPart : otherParts) { // If this permission has less parts than the other permission, everything after the number of parts contained // in this permission is automatically implied, so return true //当全部循环匹配完没有返回false,则返回true,这个getparts()方法是获取当前角色当前循环的权限标签([[one], [page1], [view]]) if (getParts().size() - 1 < i) { return true ; } else { Set<String> part = getParts().get(i); /*如果包含有‘*'而且不包含当前分割后的标签则返回 false , *当用户可以查看页面,也就是说当前角色拥有one:page1:view标签 *这里【!part.contains(WILDCARD_TOKEN)】返回 true ,第二个【part.containsAll(otherPart)】one会跟当前标签匹**配one, *也就是说这里全部循环完返回的都是 false ,所以最后都没 true ,于是在上面返回了一个 true 。 if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) { return false ; } i++; } } |
小结一下:通过分析,我们看到了shiro在定义权限标签时,要主意匹配问题,不要存在包含问题,类似aaa 和aaab ,会导致后面标签失效。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://zhengkai.blog.csdn.net/article/details/80153421