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= s e s s i o n D A O c a c h e M a n a g e r = o r g . a p a c h e . s h i r o . c a c h e . e h c a c h e . E h C a c h e M a n a g e r c a c h e M a n a g e r . c a c h e M a n a g e r C o n f i g F i l e = c l a s s p a t h : s h i r o − e h c a c h e . x m l s e c u r i t y M a n a g e r . c a c h e M a n a g e r = sessionDAO cacheManager=org.apache.shiro.cache.ehcache.EhCacheManager cacheManager.cacheManagerConfigFile=classpath:shiro-ehcache.xml securityManager.cacheManager= sessionDAOcacheManager=org.apache.shiro.cache.ehcache.EhCacheManagercacheManager.cacheManagerConfigFile=classpath:shiro−ehcache.xmlsecurityManager.cacheManager=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= s e s s i o n D A O c a c h e M a n a g e r = o r g . a p a c h e . s h i r o . c a c h e . e h c a c h e . E h C a c h e M a n a g e r c a c h e M a n a g e r . c a c h e M a n a g e r C o n f i g F i l e = c l a s s p a t h : s h i r o − e h c a c h e . x m l s e c u r i t y M a n a g e r . c a c h e M a n a g e r = sessionDAO cacheManager=org.apache.shiro.cache.ehcache.EhCacheManager cacheManager.cacheManagerConfigFile=classpath:shiro-ehcache.xml securityManager.cacheManager= sessionDAOcacheManager=org.apache.shiro.cache.ehcache.EhCacheManagercacheManager.cacheManagerConfigFile=classpath:shiro−ehcache.xmlsecurityManager.cacheManager=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.当前内容终于解决了
以上纯属个人见解,如有问题请联系本人!