spring-shiro權(quán)限控制realm實(shí)戰(zhàn)教程
spring-shiro權(quán)限控制realm
用戶與角色實(shí)體
Role.java
@Data
@Entity
public class Role {
@Id
@GeneratedValue
private Integer id;
private Long userId;
private String role;
}
User.java
@Data
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
private String password;
}
Realm類
首先建立 Realm 類,繼承自 AuthorizingRealm,自定義我們自己的授權(quán)和認(rèn)證的方法。Realm 是可以訪問特定于應(yīng)用程序的安全性數(shù)據(jù)(如用戶,角色和權(quán)限)的組件。
Realm.java
public class Realm extends AuthorizingRealm {
@Autowired
private UserService userService;
//授權(quán)
@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;
}
//認(rèn)證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//獲得當(dāng)前用戶的用戶名
String username = (String) authenticationToken.getPrincipal();
//從數(shù)據(jù)庫中根據(jù)用戶名查找用戶
User user = userService.getUserByUserName(username);
if (userService.getUserByUserName(username) == null) {
throw new UnknownAccountException(
"沒有在本系統(tǒng)中找到對應(yīng)的用戶信息。");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),getName());
return info;
}
}
Shiro 配置類
ShiroConfig.java
@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)站圖標(biāo)
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
@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("沒有足夠的權(quán)限");
}
return "home";
}
@GetMapping("/logout")
public String logout() {
return "index";
}
}
Service
UserService.java
@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權(quán)限不生效原因分析
shiro遇到的坑
-項(xiàng)目中使用shiro做登錄校驗(yàn)和權(quán)限管理,在配置權(quán)限時(shí)遇到小坑,記錄一下。
- 環(huán)境:springboot+freemarker+shiro
- 場景:后臺(tái)管理,配置菜單以及按鈕權(quán)限,分為三個(gè)層級,一二級暫時(shí)只考慮是否查看權(quán)限,第三層級為頁面按鈕權(quán)限,分增刪改查。詳情看圖
- 問題:一二層級正常,第三層級權(quán)限不起作用!
權(quán)限標(biāo)簽定義如下:
| 標(biāo)簽定義 | 頁面一 | 頁面二 |
|---|---|---|
| 第一層級 | one:view | two:view |
| 第二層級 | one:page1:view | two:page2:view |
| 第三層級 | one:page1:view:add | two:page2:view:add |
開始懷疑是數(shù)據(jù)庫沒有錄入,查看后權(quán)限標(biāo)簽與角色已對應(yīng),排除。
后面懷疑是頁面問題,后面把第三層級標(biāo)簽與第一二層級同一頁面,依然不起作用,排除。
后面懷疑是權(quán)限標(biāo)簽定義問題,把第三層級標(biāo)簽改為one:page1:data:add,奇跡出現(xiàn),權(quán)限生效。證實(shí)權(quán)限標(biāo)簽定義出了問題。
問題原因:權(quán)限標(biāo)簽定義問題
但是后來想想為什么會(huì)出現(xiàn)這種問題,每個(gè)標(biāo)簽都是獨(dú)一無二的,對此我對shiro對于權(quán)限標(biāo)簽的校驗(yàn)產(chǎn)生了興趣,查看源碼,一路debug后最終在org.apache.shiro.authz.permission中看到了關(guān)鍵所在,核心代碼如下
//當(dāng)這個(gè)方法返回true時(shí)說明有此權(quán)限
//這個(gè)p是代表當(dāng)前循環(huán)匹配到的權(quán)限標(biāo)簽
public boolean implies(Permission p) {
// By default only supports comparisons with other WildcardPermissions
if (!(p instanceof WildcardPermission)) {
return false;
}
WildcardPermission wp = (WildcardPermission) p;
//把當(dāng)前標(biāo)簽轉(zhuǎn)分割成一個(gè)set集合(如one:page1:view:add 會(huì)分割成[[one], [page1], [view], [add]])
List<Set<String>> otherParts = wp.getParts();
int i = 0;
//循環(huán)匹配權(quán)限標(biāo)簽
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
//當(dāng)全部循環(huán)匹配完沒有返回false,則返回true,這個(gè)getparts()方法是獲取當(dāng)前角色當(dāng)前循環(huán)的權(quán)限標(biāo)簽([[one], [page1], [view]])
if (getParts().size() - 1 < i) {
return true;
} else {
Set<String> part = getParts().get(i);
/*如果包含有‘*'而且不包含當(dāng)前分割后的標(biāo)簽則返回false,
*當(dāng)用戶可以查看頁面,也就是說當(dāng)前角色擁有one:page1:view標(biāo)簽
*這里【!part.contains(WILDCARD_TOKEN)】返回true,第二個(gè)【part.containsAll(otherPart)】one會(huì)跟當(dāng)前標(biāo)簽匹**配one,
*也就是說這里全部循環(huán)完返回的都是false,所以最后都沒true,于是在上面返回了一個(gè)true。
if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) {
return false;
}
i++;
}
}
小結(jié)一下:通過分析,我們看到了shiro在定義權(quán)限標(biāo)簽時(shí),要主意匹配問題,不要存在包含問題,類似aaa 和aaab ,會(huì)導(dǎo)致后面標(biāo)簽失效。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Boot自定義favicon實(shí)現(xiàn)方法實(shí)例解析
這篇文章主要介紹了Spring Boot自定義favicon實(shí)現(xiàn)方法實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
Apache Commons Math3探索之多項(xiàng)式曲線擬合實(shí)現(xiàn)代碼
這篇文章主要介紹了Apache Commons Math3探索之多項(xiàng)式曲線擬合實(shí)現(xiàn)代碼,小編覺得挺不錯(cuò)的,這里分享給大家,供需要的朋友參考。2017-10-10
Spring動(dòng)態(tài)注冊多數(shù)據(jù)源的實(shí)現(xiàn)方法
這篇文章主要介紹了Spring動(dòng)態(tài)注冊多數(shù)據(jù)源的實(shí)現(xiàn)方法,小編覺的挺不錯(cuò)的,現(xiàn)分享到腳本之家平臺(tái),需要的朋友可以參考下2018-01-01
Java編程小實(shí)例—數(shù)字時(shí)鐘的實(shí)現(xiàn)代碼示例
正所謂拳不離手曲不離口,java學(xué)習(xí)的過程中,練習(xí)還是要多一點(diǎn)比較好。接下來分享給大家一個(gè)Java編程的小實(shí)例,供朋友們參考。2017-10-10

