spring boot 整合 shiro 框架

偏执的太偏执、 2024-04-01 13:38 167阅读 0赞

1、整合shiro

1.1、创建spring boot项目
1.2、引入依赖
  1. <dependency>
  2. <groupId>org.apache.shiro</groupId>
  3. <artifactId>shiro-spring-boot-web-starter</artifactId>
  4. <version>1.9.0</version>
  5. </dependency>
  6. <!--mybatis-plus-->
  7. <dependency>
  8. <groupId>com.baomidou</groupId>
  9. <artifactId>mybatis-plus-boot-starter</artifactId>
  10. <version>3.0.5</version>
  11. </dependency>
  12. <!--mysql-->
  13. <dependency>
  14. <groupId>mysql</groupId>
  15. <artifactId>mysql-connector-java</artifactId>
  16. <version>5.1.46</version>
  17. </dependency>
  18. <dependency>
  19. <groupId>org.projectlombok</groupId>
  20. <artifactId>lombok</artifactId>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.springframework.boot</groupId>
  24. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  25. </dependency>
1.3、添加配置文件
  1. mybatis-plus:
  2. configuration:
  3. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  4. mapper-locations: classpath:mapper/*.xml
  5. spring:
  6. datasource:
  7. type: com.zaxxer.hikari.HikariDataSource
  8. driver-class-name: com.mysql.jdbc.Driver
  9. url: jdbc:mysql://localhost:3306/shirodb?characterEncoding=utf-8&useSSL=false
  10. username: root
  11. password: zzybzb
  12. jackson:
  13. date-format: yyyy-MM-dd HH:mm:ss
  14. time-zone: GMT+8
  15. shiro:
  16. loginUrl: /myController/login
1.4、启动类添加扫描包注解

在这里插入图片描述

2、登录认证实现

2.1、库表
2.1.1、建表
  1. CREATE DATABASE IF NOT EXISTS `shirodb` CHARACTER SET utf8mb4;
  2. USE `shirodb`;
  3. CREATE TABLE `user` (
  4. `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
  5. `name` VARCHAR(30) DEFAULT NULL COMMENT '用户名',
  6. `pwd` VARCHAR(50) DEFAULT NULL COMMENT '密码',
  7. `rid` BIGINT(20) DEFAULT NULL COMMENT '角色编号',
  8. PRIMARY KEY (`id`)
  9. ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表';
2.1.2、密码加密,添加几条数据
  1. public class ShiroMD5 {
  2. public static void main(String[] args) {
  3. String password = "123456";
  4. //为了保证安全,避免被破解还可以多次迭代加密,保证数据安全
  5. Md5Hash md53 = new Md5Hash(password,"salt",3);
  6. System.out.println("md5带盐的3次加密:"+md53.toHex());
  7. }
  8. }

在这里插入图片描述

2.2、创建实体类
  1. @Data
  2. @NoArgsConstructor
  3. @AllArgsConstructor
  4. public class User implements Serializable {
  5. private Integer id;
  6. private String name;
  7. private String pwd;
  8. private Integer rid;
  9. }
2.3、创建 mapper
  1. @Mapper
  2. public interface UserMapper extends BaseMapper<User> {
  3. }
2.4、创建 service
  1. public interface UserService {
  2. //用户登录
  3. User getUserInfoByName(String name);
  4. }
  5. @Service
  6. public class UserServiceImpl implements UserService {
  7. private UserMapper userMapper;
  8. @Autowired
  9. public UserServiceImpl(UserMapper userMapper) {
  10. this.userMapper = userMapper;
  11. }
  12. //根据姓名查询
  13. @Override
  14. public User getUserInfoByName(String name) {
  15. QueryWrapper<User> wrapper = new QueryWrapper<>();
  16. wrapper.eq("name",name);
  17. User user = userMapper.selectOne(wrapper);
  18. return user;
  19. }
  20. }
2.5、自定义realm
  1. @Component
  2. public class MyRealm extends AuthorizingRealm {
  3. private UserService userService;
  4. @Autowired
  5. public MyRealm(UserService userService){
  6. this.userService = userService;
  7. }
  8. @Override
  9. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  10. return null;
  11. }
  12. //自定义登录认证方法
  13. @Override
  14. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
  15. //1获取用户身份信息
  16. String username = authenticationToken.getPrincipal().toString();
  17. //2调用业务层获取用户信息(数据库)
  18. User user = userService.getUserInfoByName(username);
  19. //3非空判断,将数据封装返回
  20. if (user != null){
  21. AuthenticationInfo info = new SimpleAuthenticationInfo(
  22. authenticationToken.getPrincipal(),
  23. user.getPwd(),
  24. ByteSource.Util.bytes("salt"),
  25. authenticationToken.getPrincipal().toString()
  26. );
  27. return info;
  28. }
  29. return null;
  30. }
  31. }
2.6、配置类
  1. @Configuration
  2. public class ShiroConfig {
  3. private MyRealm myRealm;
  4. @Autowired
  5. public ShiroConfig(MyRealm myRealm){
  6. this.myRealm = myRealm;
  7. }
  8. //配置SecurityManager
  9. @Bean
  10. public DefaultWebSecurityManager defaultWebSecurityManager(){
  11. //1创建defaultWebSecurityManager 对象
  12. DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
  13. //2创建加密对象,设置相关属性
  14. HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
  15. //2.1采用md5加密
  16. matcher.setHashAlgorithmName("MD5");
  17. //2.2迭代加密次数
  18. matcher.setHashIterations(3);
  19. //3将加密对象存储到myRealm中
  20. myRealm.setCredentialsMatcher(matcher);
  21. //4将myRealm存入defaultWebSecurityManager 对象
  22. defaultWebSecurityManager.setRealm(myRealm);
  23. return defaultWebSecurityManager;
  24. }
  25. //配置Shiro内置过滤器拦截范围
  26. @Bean
  27. public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
  28. DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
  29. //设置不认证可以访问的资源
  30. definition.addPathDefinition("/my/userLogin","anon");
  31. definition.addPathDefinition("/my/login","anon");
  32. //设置登出过滤器
  33. definition.addPathDefinition("/logout","logout");
  34. //设置需要进行登录认证的拦截范围
  35. definition.addPathDefinition("/**","authc");
  36. //添加存在用户的过滤器(rememberMe)
  37. definition.addPathDefinition("/**","user");
  38. return definition;
  39. }
  40. }
2.7、controller
  1. @Controller
  2. @RequestMapping("/my")
  3. public class MyController {
  4. @RequestMapping("/userLogin")
  5. @ResponseBody
  6. public String login(String username,String password){
  7. //1获取subject对象
  8. Subject subject = SecurityUtils.getSubject();
  9. //2封装请求数据到token
  10. AuthenticationToken token = new UsernamePasswordToken(username,password);
  11. //3调用login方法进行登录认证
  12. try {
  13. subject.login(token);
  14. return "登录成功";
  15. } catch (AuthenticationException e) {
  16. e.printStackTrace();
  17. return "登录失败";
  18. }
  19. }
  20. }
2.8、访问,测试

在这里插入图片描述在这里插入图片描述

2.9、实现前端页面
2.9.1、Thymeleaf依赖
  1. dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  4. </dependency>
2.9.2、login 页面
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h1>Shiro登录认证</h1>
  9. <br>
  10. <form action="/my/userLogin">
  11. <div>用户名:<input type="text" name="username" value=""></div>
  12. <div>密码:<input type="password" name="password" value=""></div>
  13. <div><input type="submit" value="登录"></div>
  14. </form>
  15. </body>
  16. </html>
2.9.3、添加 index 页面
  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org"
  3. xmlns:shiro="http://www.w3.org/1999/xhtml">
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>Title</title>
  7. </head>
  8. <body>
  9. <h1>Shiro登录认证后主页面</h1>
  10. <br>
  11. 登录用户为:<span th:text="${session.user}"></span>
  12. </body>
2.9.4、添加 controller 方法,改造认证方法
  1. //跳转登录页面
  2. @RequestMapping("/login")
  3. public String login(){
  4. return "login";
  5. }

在这里插入图片描述

2.9.5、修改配置文件

在这里插入图片描述

2.9.6、修改配置类

在这里插入图片描述

2.9.7、访问,测试

在这里插入图片描述
在这里插入图片描述

3、多个 realm 的认证策略设置

3.1、多个realm实现原理

当应用程序配置多个 Realm 时,例如:用户名密码校验、手机号验证码校验等等。
Shiro 的 ModularRealmAuthenticator 会使用内部的 AuthenticationStrategy 组件判断认证是成功还是失败。
AuthenticationStrategy 是一个无状态的组件,它在身份验证尝试中被询问 4 次(这4 次交互所需的任何必要的状态将被作为方法参数):

  1. 在所有 Realm 被调用之前
  2. 在调用 Realm 的 getAuthenticationInfo 方法之前
  3. 在调用 Realm 的 getAuthenticationInfo 方法之后
  4. 在所有 Realm 被调用之后

认证策略的另外一项工作就是聚合所有 Realm 的结果信息封装至一个
AuthenticationInfo 实例中,并将此信息返回,以此作为 Subject 的身份信息。

Shiro 中定义了 3 种认证策略的实现:

  • AtLeastOneSuccessfulStrategy
    只要有一个(或更多)的 Realm 验证成功,那么认证将视为成功。
  • FirstSuccessfulStrategy
    第一个 Realm 验证成功,整体认证将视为成功,且后续 Realm 将被忽略。
  • AllSuccessfulStrategy
    所有 Realm 成功,认证才视为成功。

ModularRealmAuthenticator 内置的认证策略默认实现是
AtLeastOneSuccessfulStrategy 方式。可以通过配置修改策略。

3.2、多个realm代码实现
  1. //配置 SecurityManager
  2. @Bean
  3. public DefaultWebSecurityManager defaultWebSecurityManager(){
  4. //1 创建 defaultWebSecurityManager 对象
  5. DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
  6. //2 创建认证对象,并设置认证策略
  7. ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
  8. modularRealmAuthenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
  9. defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator);
  10. //3 封装 myRealm 集合
  11. List<Realm> list = new ArrayList<>();
  12. list.add(myRealm);
  13. list.add(myRealm2);
  14. //4 将 myRealm 存入 defaultWebSecurityManager 对象
  15. defaultWebSecurityManager.setRealms(list);
  16. //5 返回
  17. return defaultWebSecurityManager;
  18. }

4、remember me 功能

Shiro 提供了记住我(RememberMe)的功能,比如访问一些网站时,关闭了浏览器,下次再打开时还是能记住你是谁, 下次访问时无需再登录即可访问。

4.1、基本流程
  1. 首先在登录页面选中 RememberMe 然后登录成功;如果是浏览器登录,一般会 把 RememberMe 的 Cookie 写到客户端并保存下来。
  2. 关闭浏览器再重新打开;会发现浏览器还是记住你的。
  3. 访问一般的网页服务器端,仍然知道你是谁,且能正常访问。
  4. 但是,如果我们访问电商平台时,如果要查看我的订单或进行支付时,此时还是需要再进行身份认证的,以确保当前用户还是你。
4.2、代码实现
4.2.1、修改配置类
  1. //cookie属性设置
  2. public SimpleCookie rememberMeCookie(){
  3. SimpleCookie cookie = new SimpleCookie("rememberMe");
  4. //设置跨域
  5. //cookie.setDomain(domain);
  6. cookie.setPath("/");
  7. cookie.setHttpOnly(true);
  8. cookie.setMaxAge(30*24*60*60);
  9. return cookie;
  10. }
  11. //创建Shiro的cookie管理对象
  12. public CookieRememberMeManager rememberMeManager(){
  13. CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
  14. cookieRememberMeManager.setCookie(rememberMeCookie());
  15. cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
  16. return cookieRememberMeManager;
  17. }
  18. defaultWebSecurityManager.setRememberMeManager(rememberMeManager());

在这里插入图片描述
在这里插入图片描述

4.2.2、添加,修改 controller

添加

  1. //登录认证验证 rememberMe
  2. @GetMapping("userLoginRm")
  3. public String userLogin(HttpSession session) {
  4. session.setAttribute("user","rememberMe");
  5. return "index";
  6. }

修改
在这里插入图片描述

4.2.3、修改登录页面
  1. <div>记住用户:<input type="checkbox" name="rememberMe" value="true"></div>

在这里插入图片描述

4.3、访问测试
4.3.1、未勾选
  • 登录
    \- -

在这里插入图片描述

  • 访问userLoginRm
    在这里插入图片描述
  • 关掉浏览器后打开,重新访问
    在这里插入图片描述
4.3.2、勾选“记住我”
  • 登录

在这里插入图片描述
在这里插入图片描述

  • 关掉浏览器再访问
    在这里插入图片描述

5、退出

用户登录后,配套的有登出操作。直接通过Shiro过滤器即可实现登出。

5.1、index页面
  1. <a href="/logout">登出</a>
5.2、修改配置类
  1. //设置登出过滤器
  2. definition.addPathDefinition("/logout","logout");

在这里插入图片描述

5.3、测试

在这里插入图片描述在这里插入图片描述

6、授权、角色认证

6.1、授权

用户登录后,需要验证是否具有指定角色指定权限。Shiro也提供了方便的工具进行判断。这个工具就是Realm的doGetAuthorizationInfo方法进行判断。

触发权限判断的有两种方式

  1. 在页面中通过shiro:****属性判断
  2. 在接口服务中通过注解@Requires****进行判断
6.2、后端接口服务注解

通过给接口服务方法添加注解可以实现权限校验,可以加在控制器方法上,也可以加在业务方法上,一般加在控制器方法上。常用注解如下:

  • @RequiresAuthentication
    验证用户是否登录,等同于方法subject.isAuthenticated()
  • @RequiresUser
    验证用户是否被记忆:
    登录认证成功subject.isAuthenticated()为true。
    登录后被记忆subject.isRemembered()为true。
  • @RequiresGuest
    验证是否是一个guest的请求,是否是游客的请求。
    此时subject.getPrincipal()为null。
  • @RequiresRoles

    验证subject是否有相应角色,有角色访问方法,没有则会抛出异常AuthorizationException。
    例如:

    @RequiresRoles(“aRoleName”)//只有subject有aRoleName角色才能访问方法someMethod()
    void someMethod();

  • @RequiresPermissions
    验证subject是否有相应权限,有权限访问方法,没有则会抛出异常AuthorizationException。
    例如:

    @RequiresPermissions (“file:read”,”wite:aFile.txt”)//subject必须同时含有file:read和wite:aFile.txt权限才能访问方法someMethod()
    void someMethod();

6.3、授权验证-没有角色无法访问
6.3.1、controller代码
  1. //登录认证验证角色
  2. @RequiresRoles("admin")
  3. @GetMapping("userLoginRoles")
  4. @ResponseBody
  5. public String userLoginRoles(){
  6. System.out.println("登录认证验证角色");
  7. return "验证角色成功";
  8. }
6.3.2、index页面
  1. <a shiro:hasRole="admin" href="/my/userLoginRoles">测试授权-角色验证</a>
6.3.3、修改MyRealm类的doGetAuthorizationInfo方法

在这里插入图片描述

6.3.4、测试,没有admin角色,会报错

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.4、授权验证-获取角色进行验证
6.4.1、修改 MyRealm 方法

自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比

  1. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  2. //自定义添加角色
  3. info.addRole("admin");

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

6.4.2、角色表,用户-角色中间表
  1. CREATE TABLE `role` (
  2. `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
  3. `name` VARCHAR(30) DEFAULT NULL COMMENT '角色名',
  4. `desc` VARCHAR(50) DEFAULT NULL COMMENT '描述',
  5. `realname` VARCHAR(20) DEFAULT NULL COMMENT '角色显示名',
  6. PRIMARY KEY (`id`)
  7. ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色表';
  8. CREATE TABLE `role_user` (
  9. `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
  10. `uid` BIGINT(20) DEFAULT NULL COMMENT '用户 id',
  11. `rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
  12. PRIMARY KEY (`id`)
  13. ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色用户映射
  14. 表';

自定义数据–角色表
在这里插入图片描述

自定义数据–用户角色中间表
在这里插入图片描述

6.4.3、查询 sql

根据用户名查询对应角色信息

  1. SELECT NAME FROM role WHERE id IN (SELECT rid FROM role_user WHERE
  2. uid=(SELECT id FROM USER WHERE NAME='zhangsan'));

在这里插入图片描述

6.4.4、mapper 方法
  1. @Select("SELECT NAME FROM role WHERE id IN (SELECT rid FROM role_user WHERE uid=(SELECT id FROM USER WHERE NAME=#{principal}))")
  2. List<String> getUserRoleInfoMapper(@Param("principal")String principal );
6.4.5、service 方法
  1. /**
  2. * 根据用户查询角色
  3. * @param principal
  4. * @return
  5. */
  6. @Override
  7. public List<String> getUserRoleInfo(String principal) {
  8. return userMapper.getUserRoleInfoMapper(principal);
  9. }
6.4.6、MyRealm 方法改造
  1. @Override
  2. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  3. System.out.println("进入自定义授权方法");
  4. //1获取用户身份信息
  5. String principal = principalCollection.getPrimaryPrincipal().toString();
  6. //2调用业务层获取用户的角色信息(数据库)
  7. List<String> roles = userService.getUserRoleInfo(principal);
  8. System.out.println("当前用户角色信息 = " + roles);
  9. //2.5调用业务层获取用户的权限信息(数据库)
  10. //3创建对象,封装当前登录用户的角色、权限信息
  11. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  12. //自定义添加角色
  13. info.addRoles(roles);
  14. //返回信息
  15. return info;
  16. }
6.4.7、启动测试

在这里插入图片描述在这里插入图片描述

6.5、授权验证-获取权限进行验证
6.5.1、权限表(菜单表),角色权限中间表
  1. CREATE TABLE `permissions` (
  2. `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
  3. `name` VARCHAR(30) DEFAULT NULL COMMENT '权限名',
  4. `info` VARCHAR(30) DEFAULT NULL COMMENT '权限信息',
  5. `desc` VARCHAR(50) DEFAULT NULL COMMENT '描述',
  6. PRIMARY KEY (`id`)
  7. ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='权限表';
  8. CREATE TABLE `role_ps` (
  9. `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
  10. `rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
  11. `pid` BIGINT(20) DEFAULT NULL COMMENT '权限 id',
  12. PRIMARY KEY (`id`)
  13. ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色权限映射表';

自定义数据–权限表
在这里插入图片描述

自定义数据–角色权限表

在这里插入图片描述

6.5.2、查询 sql

根据角色名查询对应权限信息

  1. SELECT info FROM permissions WHERE id IN (SELECT pid FROM role_ps WHERE rid
  2. IN (SELECT id FROM role WHERE NAME IN('admin','user')));
6.5.3、mapper 方法
  1. @Select({
  2. "<script>",
  3. "select info FROM permissions WHERE id IN ",
  4. "(SELECT pid FROM role_ps WHERE rid IN (",
  5. "SELECT id FROM role WHERE NAME IN ",
  6. "<foreach collection='roles' item='name' open='(' separator=',' close=')'>",
  7. "#{name}",
  8. "</foreach>",
  9. "))",
  10. "</script>"
  11. })
  12. List<String> getUserPermissionInfoMapper(@Param("roles")List<String> roles);
6.5.4、service代码
  1. /**
  2. * 根据角色查看权限
  3. * @param roles
  4. * @return
  5. */
  6. @Override
  7. public List<String> getUserPermissionInfo(List<String> roles) {
  8. return userMapper.getUserPermissionInfoMapper(roles);
  9. }
6.5.5、realm代码

在这里插入图片描述

6.5.6、controller 方法
  1. //登录认证验证权限
  2. @RequiresPermissions("user:del")
  3. @GetMapping("userPermissions")
  4. @ResponseBody
  5. public String userLoginPermissions(){
  6. System.out.println("登录认证验证权限");
  7. return "验证权限成功";
  8. }
6.5.7、index页面
  1. <a shiro:hasPermission="user:delete" href="/my/userPermissions">测试授权-权限验证</a>
6.5.8、测试

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

6.6、授权验证-异常处理

创建认证异常处理类,使用@ControllerAdvice 加@ExceptionHandler 实现特殊异常处理。

  1. package com.bz.shiro.controller;
  2. import org.apache.shiro.authz.AuthorizationException;
  3. import org.apache.shiro.authz.UnauthorizedException;
  4. import org.springframework.web.bind.annotation.ControllerAdvice;
  5. import org.springframework.web.bind.annotation.ExceptionHandler;
  6. import org.springframework.web.bind.annotation.ResponseBody;
  7. @ControllerAdvice
  8. public class PermissionsException {
  9. @ResponseBody
  10. @ExceptionHandler(UnauthorizedException.class)
  11. public String unauthorizedException(Exception e){
  12. return "无权限";
  13. }
  14. @ResponseBody
  15. @ExceptionHandler(AuthorizationException.class)
  16. public String authorizationException(Exception e){
  17. return "权限认证失败";
  18. }
  19. }

lisi账号登录
在这里插入图片描述在这里插入图片描述

6.7、前页面授权验证
6.7.1、引入依赖
  1. <!--配置Thymeleaf与Shrio的整合依赖-->
  2. <dependency>
  3. <groupId>com.github.theborakompanioni</groupId>
  4. <artifactId>thymeleaf-extras-shiro</artifactId>
  5. <version>2.0.0</version>
  6. </dependency>
6.7.2、配置类添加新配置
  1. @Bean
  2. public ShiroDialect shiroDialect(){
  3. return new ShiroDialect();
  4. }
6.7.3、Thymeleaf 中常用的 shiro:属性
  • guest 标签
    用户没有身份验证时显示相应信息,即游客访问信息。

  • user 标签
    用户已经身份验证/记住我登录后显示相应的信息。

  • authenticated 标签
    用户已经身份验证通过,即 Subject.login 登录成功,不是记住我登录的。

  • notAuthenticated 标签
    用户已经身份验证通过,即没有调用 Subject.login 进行登录,包括记住我自动登录的也属于未进行身份验证。

  • principal 标签
    相当于((User)Subject.getPrincipals()).getUsername()。

  • lacksPermission 标签
    如果当前 Subject 没有权限将显示 body 体内容。

  • hasRole 标签
    如果当前 Subject 有角色将显示 body 体内容。

  • hasAnyRoles 标签
    如果当前 Subject 有任意一个角色(或的关系)将显示 body 体内容。

  • lacksRole 标签
    如果当前 Subject 没有角色将显示 body 体内容。

  • hasPermission 标签
    如果当前 Subject 有权限将显示 body 体内容

6.7.4、修改index页面

在这里插入图片描述在这里插入图片描述

  • zhangsan登录

在这里插入图片描述
在这里插入图片描述

  • lisi登录
    在这里插入图片描述在这里插入图片描述

7、实现缓存

7.1、缓存工具EhCache

EhCache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。可以和大部分Java项目无缝整合,例如:Hibernate中的缓存就是基于EhCache实现的。

EhCache支持内存和磁盘存储,默认存储在内存中,如内存不够时把缓存数据同步到磁盘中。EhCache支持基于Filter的Cache实现,也支持Gzip压缩算法。

EhCache直接在JVM虚拟机中缓存,速度快,效率高;
EhCache缺点是缓存共享麻烦,集群分布式应用使用不方便。

7.2、新建项目
7.2.1、引入依赖
  1. <dependency>
  2. <groupId>net.sf.ehcache</groupId>
  3. <artifactId>ehcache</artifactId>
  4. <version>2.6.11</version>
  5. <type>pom</type>
  6. </dependency>
7.2.2、添加配置文件 ehcache.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <ehcache>
  3. <!--磁盘的缓存位置-->
  4. <diskStore path="java.io.tmpdir/ehcache"/>
  5. <!--默认缓存-->
  6. <defaultCache
  7. maxEntriesLocalHeap="10000"
  8. eternal="false"
  9. timeToIdleSeconds="120"
  10. timeToLiveSeconds="120"
  11. maxEntriesLocalDisk="10000000"
  12. diskExpiryThreadIntervalSeconds="120"
  13. memoryStoreEvictionPolicy="LRU">
  14. <persistence strategy="localTempSwap"/>
  15. </defaultCache>
  16. <!--helloworld缓存-->
  17. <cache name="HelloWorldCache"
  18. maxElementsInMemory="1000"
  19. eternal="false"
  20. timeToIdleSeconds="5"
  21. timeToLiveSeconds="5"
  22. overflowToDisk="false"
  23. memoryStoreEvictionPolicy="LRU"/>
  24. <!--
  25. defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
  26. -->
  27. <!--
  28. name:缓存名称。
  29. maxElementsInMemory:缓存最大数目
  30. maxElementsOnDisk:硬盘最大缓存个数。
  31. eternal:对象是否永久有效,一但设置了,timeout将不起作用。
  32. overflowToDisk:是否保存到磁盘,当系统宕机时
  33. timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
  34. timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
  35. diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
  36. diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
  37. diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
  38. memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
  39. clearOnFlush:内存数量最大时是否清除。
  40. memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
  41. FIFO,first in first out,这个是大家最熟的,先进先出。
  42. LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
  43. LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
  44. -->
  45. </ehcache>
7.2.3、测试类
  1. import net.sf.ehcache.Cache;
  2. import net.sf.ehcache.CacheManager;
  3. import net.sf.ehcache.Element;
  4. import java.io.InputStream;
  5. public class TestEH {
  6. public static void main(String[] args) {
  7. //获取编译目录下的资源的流对象
  8. InputStream input =
  9. TestEH.class.getClassLoader().getResourceAsStream("ehcache.xml");
  10. //获取EhCache的缓存管理对象
  11. CacheManager cacheManager = new CacheManager(input);
  12. //获取缓存对象
  13. Cache cache = cacheManager.getCache("HelloWorldCache");
  14. //创建缓存数据
  15. Element element = new Element("name","zhang3");
  16. //存入缓存
  17. cache.put(element);
  18. //从缓存中取出数据输出
  19. Element element1 = cache.get("name");
  20. System.out.println("缓存中数据 = " + element1.getObjectValue());
  21. }
  22. }
7.2.4、启动

在这里插入图片描述

7.3、Shiro整合EhCache

Shiro官方提供了shiro-ehcache,实现了整合EhCache作为Shiro的缓存工具。可以缓存认证执行的Realm方法,减少对数据库的访问,提高认证效率。

7.3.1、引入依赖
  1. <!--Shiro整合EhCache-->
  2. <dependency>
  3. <groupId>org.apache.shiro</groupId>
  4. <artifactId>shiro-ehcache</artifactId>
  5. <version>1.4.2</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>commons-io</groupId>
  9. <artifactId>commons-io</artifactId>
  10. <version>2.6</version>
  11. </dependency>
7.3.2、在 resources 下添加配置文件 ehcache/ehcache-shiro.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <ehcache name="ehcache" updateCheck="false">
  3. <!--磁盘的缓存位置-->
  4. <diskStore path="java.io.tmpdir"/>
  5. <!--默认缓存-->
  6. <defaultCache
  7. maxEntriesLocalHeap="1000"
  8. eternal="false"
  9. timeToIdleSeconds="3600"
  10. timeToLiveSeconds="3600"
  11. overflowToDisk="false">
  12. </defaultCache>
  13. <!--登录认证信息缓存:缓存用户角色权限-->
  14. <cache name="loginRolePsCache"
  15. maxEntriesLocalHeap="2000"
  16. eternal="false"
  17. timeToIdleSeconds="600"
  18. timeToLiveSeconds="0"
  19. overflowToDisk="false"
  20. statistics="true"/>
  21. </ehcache>
7.3.3、修改配置类 ShiroConfig
  1. //缓存管理器
  2. public EhCacheManager getEhCacheManager(){
  3. EhCacheManager ehCacheManager = new EhCacheManager();
  4. InputStream is =null;
  5. try {
  6. is = ResourceUtils.getInputStreamForPath("classpath:ehcache/ehcache-shiro.xml");
  7. } catch (IOException e) {
  8. e.printStackTrace();
  9. }
  10. CacheManager cacheManager = new CacheManager(is);
  11. ehCacheManager.setCacheManager(cacheManager);
  12. return ehCacheManager;
  13. }

在这里插入图片描述在这里插入图片描述

7.3.4、测试
  • 第一次登录可以看到查询角色、权限信息
    在这里插入图片描述

在这里插入图片描述在这里插入图片描述在这里插入图片描述

  • 先清除日志,再点击角色认证、权限认证,查看日志,没有查询数据库

    在这里插入图片描述 在这里插入图片描述

8、会话管理

8.1、SessionManager

会话管理器,负责创建和管理用户的会话(Session)生命周期,它能够在任何环境中在本地管理用户会话,即使没有Web/Servlet/EJB容器,也一样可以保存会话。默认情况下,Shiro会检测当前环境中现有的会话机制(比如Servlet容器)进行适配,如果没有(比如独立应用程序或者非Web环境),它将会使用内置的企业会话管理器来提供相应的会话管理服务,其中还涉及一个名为SessionDAO的对象。SessionDAO负责Session的持久化操作(CRUD),允许Session数据写入到后端持久化数据库。

8.2、会话管理实现

SessionManager由SecurityManager管理。
Shiro提供了三种实现
在这里插入图片描述

  1. DefaultSessionManager:用于JavaSE环境
  2. ServletContainerSessionManager:用于web环境,直接使用Servlet容器的会话
  3. DefaultWebSessionManager:用于web环境,自己维护会话(不使用Servlet容器的 会话管理)。
8.3、获得session方式
  1. 实现

    Session session = SecurityUtils.getSubject().getSession();
    session.setAttribute(“key”,”value”)

  2. 说明

    Controller 中的 request,在 shiro 过滤器中的 doFilerInternal 方法,被包装成ShiroHttpServletRequest。

    SecurityManager 和 SessionManager 会话管理器决定 session 来源于 ServletRequest还是由 Shiro 管理的会话。

    无论是通过 request.getSession 或 subject.getSession 获取到 session,操作session,两者都是等价的。

结束!!!

  1. 人应尊敬他自己,并应自视能配得上最高尚的东西。——黑格尔

发表评论

表情:
评论列表 (有 0 条评论,167人围观)

还没有评论,来说两句吧...

相关阅读