上篇的地址:https://blog.csdn.net/a745233700/article/details/81350191
针对上一篇授权的时候频繁查询数据库的问题,可以使用shiro缓存来解决。
1、缓存流程:
(1)shiro中提供了对认证信息和授权信息的缓存。shiro默认是关闭认证信息缓存的,对于授权信息的缓存shiro默认开启的。我们主要研究授权信息缓存,因为授权的数据量大。
(2)流程:用户认证通过之后,该用户第一次授权;调用realm查询数据库。该用户第二次授权,不调用realm查询数据库,直接从缓存中取出授权信息(权限标识符)。
2、配置shiro缓存:
(1)导入依赖:
org.apache.shiro
shiro-ehcache
1.2.3
(2)配合cacheManager:
(3)编写缓存相关信息文件:shiro-ehcache.xml
至此,Shiro的缓存就配置好了。
3、缓存清空:
(1)如果用户正常退出,缓存自动清空;如果用户非正常退出,缓存也自动清空。
如果修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。
(2)手动进行编程的实现:
在权限修改后,调用realm的clearCache方法清除缓存。
下边的代码正常开发时要放在service中调用。
在service中,权限修改后调用realm的方法。
在realm中定义clearCached方法:
//清除缓存代码
public void clearCached() {
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
}
(3)测试清除缓存的controller方法:
@Controller
public class ClearShiroCacheTest {
//注入realm
@Autowired
private CustomRealm customRealm;
@RequestMapping("/clearShiroCache")
public String clearShiroCache(){
//清除缓存,在正常开发中要在service
customRealm.clearCached();
return "success";
}
}
4、shiro缓存说明:
(1)CacheManagerAware接口:
Shiro内部相关组件(DefaultSecurityManager)会自动检测相应的对象(如Realm)是否实现了CacheManagerAware并自动注入了CacheManager。
(2)Realm缓存:Shiro提供了CachingRealm,其实现了CacheManagerAware接口,提供了一些基础实现;并且,AuthenticationRealm和AuthorizingRealm也分别提供了AuthenticationInfo和AutuorizationInfo信息的缓存。
SSM和Shiro整合后,使用shiro的session管理,shiro提供sessionDao操作会话数据。
1、Shiro提供了完整的企业级会话管理功能,不依赖于底层容器(如web容器tomcat),不管JavaSE还是JavaEE环境都可以使用,提供了会话管理、会话事件监听、会话存储/持久化、容器无关的集群、失效/过期支持、对Web 的透明支持、SSO 单点登录的支持等特性。
2、配置sessionManager:
3、会话相关的API:
(1)Subject.getSession():即可获取会话;其等价于Subject.getSession(true),即如果当前没有创建Session 对象会创建一个;Subject.getSession(false),如果当前没有创建Session 则返回null。
(2)session.getId():获取当前会话的唯一标识。
(3)session.getHost():获取当前Subject的主机地址
(4)session.getTimeout() & session.setTimeout(毫秒):获取/设置当前Session的过期时间。
(5)session.getStartTimestamp() & session.getLastAccessTime():获取会话的启动时间及最后访问时间;如果是JavaSE应用需要自己定期调用session.touch() 去更新最后访问时间;如果是Web 应用,每次进入ShiroFilter都会自动调用session.touch() 来更新最后访问时间。
(6)session.touch() & session.stop():更新会话最后访问时间及销毁会话;当Subject.logout()时会自动调用stop 方法来销毁会话。如果在web中,调用HttpSession. invalidate() 也会自动调用ShiroSession.stop方法进行销毁Shiro的会话。
(7)session.setAttribute(key, val) & session.getAttribute(key) & session.removeAttribute(key):设置/获取/删除会话属性;在整个会话范围内都可以对这些属性进行操作。
1、需求:使用自定义的FormAuthenticationFilter实现验证码功能。
(1)shiro使用FormAuthenticationFilter进行表单认证,验证校验的功能应该加在FormAuthenticationFilter中,在认证之前进行验证码校验。
(2)需要写FormAuthenticationFilter的子类,继承FormAuthenticationFilter,改写它的认证方法,在认证之前进行验证码校验。
2、自定义FormAuthenticationFilter:
/**
* Description:自定义FormAuthenticationFilter,认证之前实现 验证码校验
*/
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
//原FormAuthenticationFilter的认证方法
@Override
protected boolean onAccessDenied(ServletRequest request,
ServletResponse response) throws Exception {
//在这里进行验证码的校验
//从session获取正确验证码
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpSession session =httpServletRequest.getSession();
//取出session的验证码(正确的验证码)
String validateCode = (String) session.getAttribute("validateCode");
//取出页面的验证码
//输入的验证和session中的验证进行对比
String randomcode = httpServletRequest.getParameter("randomcode");
if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
//如果校验失败,将验证码错误失败信息,通过shiroLoginFailure设置到request中
httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
//拒绝访问,不再校验账号和密码
return true;
}
return super.onAccessDenied(request, response);
}
}
3、配置自定义FormAuthenticationFilter:
4、在登陆页面添加验证码:
5、在filter配置匿名访问验证码的jsp:
至此,使用自定义FormAuthenticationFilter实现验证码功能就完成了。
1、概述:Shiro提供了记住我(RememberMe)的功能,比如访问如淘宝等一些网站时,关闭了浏览器,下次再打开时还是能记住你是谁,下次访问时无需再登录即可访问,基本流程如下:
(1)首先在登录页面选中RememberMe然后登录成功;如果是浏览器登录,一般会把RememberMe的Cookie 写到客户端并保存下来;
(2)关闭浏览器再重新打开;会发现浏览器还是记住你的;
(3)访问一般的网页服务器端还是知道你是谁,且能正常访问;
(4)但是比如我们访问淘宝时,如果要查看我的订单或进行支付时,此时还是需要再进行身份认证的,以确保当前用户还是你。
2、认证和记住我:
(1)subject.isAuthenticated() 表示用户进行了身份验证登录的,即使有Subject.login进行了登录;
(2)subject.isRemembered():表示用户是通过记住我登录的,此时可能并不是真正的你(如你的朋友使用你的电脑,或者你的cookie 被窃取)在访问的。
(3)两者二选一,即subject.isAuthenticated()==true,则subject.isRemembered()==false;反之一样。
3、实现:
用户登陆选择“记住我",本次登陆成功会向cookie写身份信息,下次登陆从cookie中取出身份信息实现自动登陆。
(1)用户身份实现java.io.Seriializable接口:
向cookie记录身份信息,需要用户身份信息对象实现序列化接口,如下:
/**
* 用户身份信息,存入session,由于tomcat将session序列化在本地硬盘上,所以使用Serializable接口
*/
public class ActiveUser implements java.io.Serializable {
private String userid;//用户id(主键)
private String usercode;// 用户账号
private String username;// 用户名称
private List menus;// 菜单
private List permissions;// 权限
//下面省略set和get方法
}
(2)配置rememberMeManager:
(3)登陆页面:
(4)配置rememberMe的名称:
(5)测试:
勾选“记住我”登陆后,查看cookie是否有rememberMe:
(6)使用UserFilter:
如果设置记住我,下次访问某些url时可以不用登陆。将记住我即可访问的地址配置让UserFilter拦截。
至此,Shiro的rememberMe就配置完成了。
(7)remenberMe使用建议:
①访问一般网页:如个人在主页之类的,我们使用user 拦截器即可,user 拦截器只要用户登录(isRemembered() || isAuthenticated())过即可访问成功;
②访问特殊网页:如我的订单,提交订单页面,我们使用authc拦截器即可,authc拦截器会判断用户是否是通过Subject.login(isAuthenticated()==true)登录的,如果是才放行,否则会跳转到登录页面叫你重新登录。