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 是可以訪問特定于應用程序的安全性數(shù)據(jù)(如用戶,角色和權限)的組件。
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(); //根據(jù)用戶名查詢用戶對象 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(); //從數(shù)據(jù)庫中根據(jù)用戶名查找用戶 User user = userService.getUserByUserName(username); if (userService.getUserByUserName(username) == null ) { throw new UnknownAccountException( "沒有在本系統(tǒng)中找到對應的用戶信息。" ); } 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>(); //以下是過濾鏈,按順序過濾,所以/**需要放最后 //開放的靜態(tài)資源 filterChainDefinitionMap.put( "/favicon.ico" , "anon" ); //網(wǎng)站圖標 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( "我現(xiàn)在擁有角色admin,可以執(zhí)行本條語句" ); } } |
shiro權限不生效原因分析
shiro遇到的坑
-項目中使用shiro做登錄校驗和權限管理,在配置權限時遇到小坑,記錄一下。
- 環(huán)境:springboot+freemarker+shiro
- 場景:后臺管理,配置菜單以及按鈕權限,分為三個層級,一二級暫時只考慮是否查看權限,第三層級為頁面按鈕權限,分增刪改查。詳情看圖
- 問題:一二層級正常,第三層級權限不起作用!
權限標簽定義如下:
標簽定義 | 頁面一 | 頁面二 |
---|---|---|
第一層級 | one:view | two:view |
第二層級 | one:page1:view | two:page2:view |
第三層級 | one:page1:view:add | two:page2:view:add |
開始懷疑是數(shù)據(jù)庫沒有錄入,查看后權限標簽與角色已對應,排除。
后面懷疑是頁面問題,后面把第三層級標簽與第一二層級同一頁面,依然不起作用,排除。
后面懷疑是權限標簽定義問題,把第三層級標簽改為one:page1:data:add,奇跡出現(xiàn),權限生效。證實權限標簽定義出了問題。
問題原因:權限標簽定義問題
但是后來想想為什么會出現(xiàn)這種問題,每個標簽都是獨一無二的,對此我對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是代表當前循環(huán)匹配到的權限標簽 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 ; //循環(huán)匹配權限標簽 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 //當全部循環(huán)匹配完沒有返回false,則返回true,這個getparts()方法是獲取當前角色當前循環(huán)的權限標簽([[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, *也就是說這里全部循環(huán)完返回的都是 false ,所以最后都沒 true ,于是在上面返回了一個 true 。 if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) { return false ; } i++; } } |
小結一下:通過分析,我們看到了shiro在定義權限標簽時,要主意匹配問題,不要存在包含問題,類似aaa 和aaab ,會導致后面標簽失效。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持服務器之家。
原文鏈接:https://zhengkai.blog.csdn.net/article/details/80153421