1.聲明
當前的內容用於本人學習和使用之用,關於本人在一次偶然的機會發現的一個bug,一個關於shiro的bug(SpringBoot中集成Shiro的時候調用logout出現的bug)
2.pom文件配置
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<!-- shiro的配置 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
<!-- 使用註解版的權限管理需要開啓aop -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-aspectj</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!-- 配置shiro的會話緩存 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.1</version>
</dependency>
springboot2.2.5.RELEASE和shiro1.4.1集成
3.ShiroConfig文件和其他的配置文件
@Configuration
@ConditionalOnClass({ SecurityManager.class, ShiroFilterFactoryBean.class, AuthorizationAttributeSourceAdvisor.class })
public class ShiroConfig {
// 啓動基於ini配置的方式認證,這裏可以使用jdbc方式進行身份的驗證
public Realm realm() {
/*
* JdbcRealm jdbcRealm = new JdbcRealm();
* jdbcRealm.setPermissionsLookupEnabled(true);
* jdbcRealm.setDataSource(dataSource);
*/
// 開啓默認自動查找權限
// 或者使用自定義的Realm方式
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
/*
* iniRealm.setAuthenticationCachingEnabled(true);
* iniRealm.setAuthorizationCachingEnabled(true);
*/
return iniRealm;
}
// 創建校驗管理器
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm());
securityManager.setSessionManager(defaultWebSessionManager());
return securityManager;
}
// 創建Shiro過濾器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Autowired DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<String, String>(); // 登出 //
//map.put("/logout", "logout"); // 默認的退出登錄方式,使用spring方式實現
map.put("/**", "authcBasic"); // 對所有用戶認證 // 登錄頁面使用get方式
shiroFilterFactoryBean.setLoginUrl("/login"); // 首頁get方式
shiroFilterFactoryBean.setSuccessUrl("/index"); // 錯誤頁面,認證不通過跳轉
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
@Autowired DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
shiro.ini配置文件
[main]
authcBasic.applicationName=please login
sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionDAO=org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionDAO.activeSessionsCacheName=shiro-activeSessionCache
sessionManager.sessionDAO=cacheManager
[users]
admin=admin,admin
user=user,user
[roles]
admin=insert,update,delete,select
user=select
[urls]
#/login=anon
#/logout=logout
#/unauthorized=anon
#/static/**=anon
#/authenticated=authc
#直接使用authcBasic,就是會彈出認證的輸入框
#/role=authcBasic,roles[admin]
#/permission=authc,perms[“user:create”]
shiro-ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<cache name="shiro-activeSessionCache"
maxEntriesLocalHeap="10000"
overflowToDisk="false"
eternal="false"
diskPersistent="false"
timeToLiveSeconds="0"
timeToIdleSeconds="0"
statistics="true"/>
</ehcache>
4.創建訪問的ShiroController
@Controller
public class ShiroController {
@RequiresRoles(value = { "admin" })
@RequestMapping("/getShiroSubject")
@org.springframework.web.bind.annotation.ResponseBody
public ResponseBody getShiroSubject() {
Subject subject = SecurityUtils.getSubject();
Object loginUser = subject.getPrincipal();
// 獲取當前登錄的用戶,如何獲取會話中的權限
return ResponseBodyBuilder.instance().ok(loginUser);
}
@RequestMapping("/getDatas")
@org.springframework.web.bind.annotation.ResponseBody
public ResponseBody getDatas() {
// 獲取當前登錄的用戶,如何獲取會話中的權限
return ResponseBodyBuilder.instance().ok("成功獲取數據");
}
// 退出shiro控制
@RequestMapping("/logout")
public String logout() {
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:login";
}
}
5.重現當前的問題logout問題
1.訪問logout
2.由於上面logout是成功的(但是這個地方卻可以訪問)
連帶有註解的@RequiresRoles(value = { "admin" }) 都成功了,這是什麼鬼?
一個非常詭異的問題產生了,本人通過debug發現確實調用了方法,也沒有報錯,但是就是shiro都是允許訪問的(這是一個bug)
再次訪問logout發現還是可以訪問
6.清空瀏覽器緩存然後再訪問
需要輸入密碼和用戶名
再次登錄和退出,以及再次訪問又發現可以訪問了
通過不停的調試和排除其他東西,查看原生的servlet中logout是可以執行的(基於ShiroFilter方式是可以實現的)
最後本人無法解決這個問題,所以本人認爲這應該就是一個bug
7.解決方案
通過本人的不屑努力,終於發現當前出現問題的地方,在於authcBasic
原因是Basic驗證時基於HTTP的和SHIRO無關!
參考:解決方案
8.修改當前的shiro.ini配置認證方式爲authc
[main]
authcBasic.applicationName=please login
authc.loginUrl=/login
sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionDAO=org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionDAO.activeSessionsCacheName=shiro-activeSessionCache
sessionManager.sessionDAO=cacheManager
sessionManager.sessionIdCookie.path=/
[users]
admin=admin,admin
user=user,user
[roles]
admin=insert,update,delete,select
user=select
[urls]
#/login=anon
修改ShiroConfig文件中的ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
Map<String, String> map = new HashMap<String, String>(); // 登出 //
//map.put("/logout", "logout"); // 默認的退出登錄方式,使用spring方式實現
map.put("/**", "authc"); // 對所有用戶認證 // 登錄頁面使用get方式
//map.put("/**", "authcBasic");
// 注意這裏的authcBasic,該驗證是BASIC認證都是需要關閉瀏覽器才清除保存的用戶認證信息,跟是否使用shiro無關!
shiroFilterFactoryBean.setLoginUrl("/login"); // 首頁get方式authc.loginUrl = /login
shiroFilterFactoryBean.setSuccessUrl("/index"); // 錯誤頁面,認證不通過跳轉
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
9.創建登錄login.html和對應的@RequestMapping
再當前的resources中創建一個文件爲login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/login" method="post">
<input type="text" name="username"/><br/>
<input type="password" name="password"/><br/>
<input type="submit" value="登錄"/>
</form>
</body>
</html>
對應的@RequestMapping
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login() {
return "login.html";
}
@RequestMapping(value = "/login", method = RequestMethod.POST)
@org.springframework.web.bind.annotation.ResponseBody
ResponseBody login(String username, String password) {
Subject subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken(username, password));
return ResponseBodyBuilder.instance().ok("登錄成功!");
}
10.測試
11.總結
1.在springboot中配置Shiro的時候,一定要注意認證方式,不同的認證方式會產生不同的效果(這裏的authcBasic就是一個只和Http有關的認證方式)
2.一般在web環境中只需要使用authc認證即可
,小心authcBasic
(雖然比較簡單,但是在前後端分離的時候容易出現問題)
2.當前內容終於解決了
以上純屬個人見解,如有問題請聯繫本人!