• SpringBoot整合Shiro


    引入依赖

    1. org.apache.shiro
    2. shiro-spring-boot-web-starter
    3. 1.9.0
    4. org.springframework.boot
    5. spring-boot-starter-thymeleaf

    配置文件

    配置文件 yml或properties,添加基础配置
    1. shiro:
    2. loginUrl: /myController/login
    3. 或者
    4. shiro.loginUrl=/myController/login

    登录认证

    访问数据库获取用户信息,实现登录认证

    后端接口服务实现

    创建库表

    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='用户表';

    创建实体

    1. import lombok.AllArgsConstructor;
    2. import lombok.Data;
    3. import lombok.NoArgsConstructor;
    4. @Data
    5. @NoArgsConstructor
    6. @AllArgsConstructor
    7. public class User {
    8. private Integer id;
    9. private String name;
    10. private String pwd;
    11. private Integer rid;
    12. }

    创建 mapper

    1. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    2. import com.example.demo.model.User;
    3. import org.springframework.stereotype.Repository;
    4. @Repository
    5. public interface UserMapper extends BaseMapper<User> {
    6. }

    创建service接口

    1. import com.example.demo.model.User;
    2. public interface UserService {
    3. //用户登录
    4. User getUserInfoByName(String name);
    5. }

    创建实现类

    1. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    2. import com.example.demo.mapper.UserMapper;
    3. import com.example.demo.model.User;
    4. import com.example.demo.service.UserService;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.stereotype.Service;
    7. @Service
    8. public class UserServiceImpl implements UserService {
    9. @Autowired
    10. private UserMapper userMapper;
    11. @Override
    12. public User getUserInfoByName(String name) {
    13. QueryWrapper wrapper = new QueryWrapper<>();
    14. wrapper.eq("name",name);
    15. User user = userMapper.selectOne(wrapper);
    16. return user;
    17. }
    18. }

    自定义realm

    1. import com.example.demo.model.User;
    2. import com.example.demo.service.UserService;
    3. import org.apache.shiro.authc.AuthenticationException;
    4. import org.apache.shiro.authc.AuthenticationInfo;
    5. import org.apache.shiro.authc.AuthenticationToken;
    6. import org.apache.shiro.authc.SimpleAuthenticationInfo;
    7. import org.apache.shiro.authz.AuthorizationInfo;
    8. import org.apache.shiro.realm.AuthorizingRealm;
    9. import org.apache.shiro.subject.PrincipalCollection;
    10. import org.apache.shiro.util.ByteSource;
    11. import org.springframework.beans.factory.annotation.Autowired;
    12. import org.springframework.stereotype.Component;
    13. @Component
    14. public class MyRealm extends AuthorizingRealm {
    15. @Autowired
    16. private UserService userService;
    17. //自定义授权方法
    18. @Override
    19. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    20. return null;
    21. }
    22. //自定义登录认证方法
    23. @Override
    24. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    25. //1 获取用户身份信息
    26. String name = token.getPrincipal().toString();
    27. //2 调用业务层获取用户信息(数据库中)
    28. User user = userService.getUserInfoByName(name);
    29. //3 判断并将数据完成封装
    30. if(user!=null){
    31. AuthenticationInfo info = new SimpleAuthenticationInfo(
    32. token.getPrincipal(),
    33. user.getPwd(),
    34. ByteSource.Util.bytes("salt"),
    35. token.getPrincipal().toString()
    36. );
    37. return info;
    38. }
    39. return null;
    40. }
    41. }

    编写配置类

    1. import com.example.demo.component.MyRealm;
    2. import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    3. import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
    4. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.context.annotation.Bean;
    7. import org.springframework.context.annotation.Configuration;
    8. @Configuration
    9. public class ShiroConfig {
    10. @Autowired
    11. private MyRealm myRealm;
    12. //配置 SecurityManager
    13. @Bean
    14. public DefaultWebSecurityManager defaultWebSecurityManager() {
    15. //1 创建 defaultWebSecurityManager 对象
    16. DefaultWebSecurityManager defaultWebSecurityManager = new
    17. DefaultWebSecurityManager();
    18. //2 创建加密对象,并设置相关属性
    19. HashedCredentialsMatcher matcher = new
    20. HashedCredentialsMatcher();
    21. //2.1 采用 md5 加密
    22. matcher.setHashAlgorithmName("md5");
    23. //2.2 迭代加密次数
    24. matcher.setHashIterations(3);
    25. //3 将加密对象存储到 myRealm 中
    26. myRealm.setCredentialsMatcher(matcher);
    27. //4 将 myRealm 存入 defaultWebSecurityManager 对象
    28. defaultWebSecurityManager.setRealm(myRealm);
    29. //5 返回
    30. return defaultWebSecurityManager;
    31. }
    32. //配置 Shiro 内置过滤器拦截范围
    33. @Bean
    34. public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() {
    35. DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
    36. //设置不认证可以访问的资源
    37. definition.addPathDefinition("/myController/userLogin", "anon");
    38. definition.addPathDefinition("/login", "anon");
    39. //设置需要进行登录认证的拦截范围
    40. definition.addPathDefinition("/**", "authc");
    41. return definition;
    42. }
    43. }

    实现controller

    1. import org.apache.shiro.SecurityUtils;
    2. import org.apache.shiro.authc.AuthenticationException;
    3. import org.apache.shiro.authc.AuthenticationToken;
    4. import org.apache.shiro.authc.UsernamePasswordToken;
    5. import org.apache.shiro.subject.Subject;
    6. import org.springframework.stereotype.Controller;
    7. import org.springframework.web.bind.annotation.GetMapping;
    8. import org.springframework.web.bind.annotation.RequestMapping;
    9. import org.springframework.web.bind.annotation.ResponseBody;
    10. @Controller
    11. @RequestMapping("myController")
    12. public class MyController {
    13. @GetMapping("userLogin")
    14. @ResponseBody
    15. public String userLogin(String name,String pwd){
    16. //1 获取 Subject 对象
    17. Subject subject = SecurityUtils.getSubject();
    18. //2 封装请求数据到 token 对象中
    19. AuthenticationToken token = new UsernamePasswordToken(name,pwd);
    20. //3 调用 login 方法进行登录认证
    21. try {
    22. subject.login(token);
    23. return "登录成功";
    24. } catch (AuthenticationException e) {
    25. e.printStackTrace();
    26. System.out.println("登录失败");
    27. return "登录失败";
    28. }
    29. }
    30. }

    测试

    确认数据库密码是加盐 3 次加密密码

    启动服务通过浏览器访问http://localhost:8080/myController/userLogin?name=张三&pwd=z3

    实现前端页面

    添加 login 页面

    1. "en">
    2. "UTF-8">
    3. Title
    4. Shiro 登录认证


    5. "/myController/userLogin">
    6. 用户名:"text" name="name" value="">
    7. 密码:"password" name="pwd" value="">
    8. "submit" value="登录">

    添加 main 页面

    1. "en" xmlns:th="http://www.thymeleaf.org">
    2. "UTF-8">
    3. Title
    4. Shiro 登录认证后主页面


    5. 登录用户为:"${session.user}">

    添加 controller 方法

    1. //跳转登录页面
    2. @GetMapping("login")
    3. public String login(){
    4. return "login";
    5. }
    6. //登录认证
    7. @GetMapping("userLogin")
    8. public String userLogin(String name, String pwd, HttpSession session){
    9. //1 获取 Subject 对象
    10. Subject subject = SecurityUtils.getSubject();
    11. //2 封装请求数据到 token 对象中
    12. AuthenticationToken token = new UsernamePasswordToken(name,pwd);
    13. //3 调用 login 方法进行登录认证
    14. try {
    15. subject.login(token);
    16. session.setAttribute("user",token.getPrincipal().toString());
    17. return "main";
    18. } catch (AuthenticationException e) {
    19. e.printStackTrace();
    20. System.out.println("登录失败");
    21. return "登录失败";
    22. }
    23. }

    修改配置类

    测试

    启动访问测试 localhost:8080/myController/login

    多个 realm 的认证策略设置 

    多个realm实现原理

    当应用程序配置多个 Realm 时,例如:用户名密码校验、手机号验证码校验等等。Shiro 的 ModularRealmAuthenticator 会使用内部的 AuthenticationStrategy 组件判断认证是成功还是失败。
    AuthenticationStrategy 是一个无状态的组件,它在身份验证尝试中被询问 4 次(这4 次交互所需的任何必要的状态将被作为方法参数):
    • 在所有 Realm 被调用之前
    • 在调用 Realm 的 getAuthenticationInfo 方法之前
    • 在调用 Realm 的 getAuthenticationInfo 方法之后
    • 在所有 Realm 被调用之后
    认证策略的另外一项工作就是聚合所有 Realm 的结果信息封装至一个AuthenticationInfo 实例中,并将此信息返回,以此作为 Subject 的身份信息。 Shiro 中定义了 3 种认证策略的实现:
    AuthenticationStrategy class
    描述
    AtLeastOneSuccessfulStrategy
    只要有一个(或更多)的 Realm 验证成功,那么认证将视为成功
    FirstSuccessfulStrategy
    第一个 Realm 验证成功,整体认证将视为成功,且后续 Realm 将被忽略
    AllSuccessfulStrategy
    所有 Realm 成功,认证才视为成功
    ModularRealmAuthenticator 内置的认证策略默认实现是AtLeastOneSuccessfulStrategy 方式。可以通过配置修改策略

    多个realm代码实现

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

    remember me 功能

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

    基本流程

    1. 首先在登录页面选中 RememberMe 然后登录成功;如果是浏览器登录,一般会把 RememberMe 的 Cookie 写到客户端并保存下来;
    2. 关闭浏览器再重新打开;会发现浏览器还是记住你的;
    3. 访问一般的网页服务器端,仍然知道你是谁,且能正常访问;
    4. 但是,如果我们访问电商平台时,如果要查看我的订单或进行支付时,此时还是需要再进行身份认证的,以确保当前用户还是你。

    代码实现 

    修改配置类
    1. import com.example.demo.component.MyRealm;
    2. import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    3. import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
    4. import org.apache.shiro.web.mgt.CookieRememberMeManager;
    5. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    6. import org.apache.shiro.web.servlet.SimpleCookie;
    7. import org.springframework.beans.factory.annotation.Autowired;
    8. import org.springframework.context.annotation.Bean;
    9. import org.springframework.context.annotation.Configuration;
    10. @Configuration
    11. public class ShiroConfig {
    12. @Autowired
    13. private MyRealm myRealm;
    14. //配置 SecurityManager
    15. @Bean
    16. public DefaultWebSecurityManager defaultWebSecurityManager() {
    17. //1 创建 defaultWebSecurityManager 对象
    18. DefaultWebSecurityManager defaultWebSecurityManager = new
    19. DefaultWebSecurityManager();
    20. //2 创建加密对象,并设置相关属性
    21. HashedCredentialsMatcher matcher = new
    22. HashedCredentialsMatcher();
    23. //2.1 采用 md5 加密
    24. matcher.setHashAlgorithmName("md5");
    25. //2.2 迭代加密次数
    26. matcher.setHashIterations(3);
    27. //3 将加密对象存储到 myRealm 中
    28. myRealm.setCredentialsMatcher(matcher);
    29. //4 将 myRealm 存入 defaultWebSecurityManager 对象
    30. defaultWebSecurityManager.setRealm(myRealm);
    31. //4.5 设置 rememberMe
    32. defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
    33. //5 返回
    34. return defaultWebSecurityManager;
    35. }
    36. //cookie 属性设置
    37. public SimpleCookie rememberMeCookie(){
    38. SimpleCookie cookie = new SimpleCookie("rememberMe");
    39. //设置跨域
    40. //cookie.setDomain(domain);
    41. cookie.setPath("/");
    42. cookie.setHttpOnly(true);
    43. cookie.setMaxAge(30*24*60*60);
    44. return cookie;
    45. }
    46. //创建 Shiro 的 cookie 管理对象
    47. public CookieRememberMeManager rememberMeManager(){
    48. CookieRememberMeManager cookieRememberMeManager = new
    49. CookieRememberMeManager();
    50. cookieRememberMeManager.setCookie(rememberMeCookie());
    51. cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
    52. return cookieRememberMeManager;
    53. }
    54. //配置 Shiro 内置过滤器拦截范围
    55. @Bean
    56. public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() {
    57. DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
    58. //设置不认证可以访问的资源
    59. definition.addPathDefinition("/myController/userLogin", "anon");
    60. definition.addPathDefinition("/myController/login", "anon");
    61. definition.addPathDefinition("/login", "anon");
    62. //设置需要进行登录认证的拦截范围
    63. definition.addPathDefinition("/**", "authc");
    64. //添加存在用户的过滤器(rememberMe)
    65. definition.addPathDefinition("/**","user");
    66. return definition;
    67. }
    68. }
    修改 controller
    1. //登录认证
    2. @GetMapping("userLogin")
    3. public String userLogin(String name, String pwd,@RequestParam(defaultValue =
    4. "false")boolean rememberMe, HttpSession session){
    5. //1 获取 Subject 对象
    6. Subject subject = SecurityUtils.getSubject();
    7. //2 封装请求数据到 token 对象中
    8. AuthenticationToken token = new UsernamePasswordToken(name,pwd,rememberMe);
    9. //3 调用 login 方法进行登录认证
    10. try {
    11. subject.login(token);
    12. session.setAttribute("user",token.getPrincipal().toString());
    13. return "main";
    14. } catch (AuthenticationException e) {
    15. e.printStackTrace();
    16. System.out.println("登录失败");
    17. return "登录失败";
    18. }
    19. }
    20. //登录认证验证 rememberMe
    21. @GetMapping("userLoginRm")
    22. public String userLogin(HttpSession session) {
    23. session.setAttribute("user","rememberMe");
    24. return "main";
    25. }
    改造页面 login.html
    1. Shiro 登录认证


    2. "/myController/userLogin">
    3. 用户名:"text" name="name" value="">
    4. 密码:"password" name="pwd" value="">
    5. 记住用户:"checkbox" name="rememberMe"
    6. value="true">
  • "submit" value="登录">
  • 测试

    通过地址访问 userLoginRm http://localhost:8080/myController/userLoginRm

登录勾选记住用户

重新访问 userLoginRm http://localhost:8080/myController/userLoginRm

用户登录认证后登出 

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

代码实现

修改登录后的 main.html
  1. Shiro 登录认证后主页面


  2. 登录用户为:"${session.user}">

修改配置类,添加 logout 过滤器
  1. //配置 Shiro 内置过滤器拦截范围
  2. @Bean
  3. public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
  4. DefaultShiroFilterChainDefinition definition = new
  5. DefaultShiroFilterChainDefinition();
  6. //设置不认证可以访问的资源
  7. definition.addPathDefinition("/myController/userLogin","anon");
  8. definition.addPathDefinition("/myController/login","anon");
  9. //配置登出过滤器
  10. definition.addPathDefinition("/logout","logout");
  11. //设置需要进行登录认证的拦截范围
  12. definition.addPathDefinition("/**","authc");
  13. //添加存在用户的过滤器(rememberMe)
  14. definition.addPathDefinition("/**","user");
  15. return definition;
  16. }

测试

点击“登出”登出系统

角色授权

授权

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

后端接口服务注解 

通过给接口服务方法添加注解可以实现权限校验,可以加在控制器方法上,也可以加在业务方法上,一般加在控制器方法上。常用注解如下:
@RequiresAuthentication
验证用户是否登录,等同于方法subject.isAuthenticated()
@RequiresUser
验证用户是否被记忆:
登录认证成功subject.isAuthenticated()为true
登录后被记忆subject.isRemembered()为true
@RequiresGuest
验证是否是一个guest的请求,是否是游客的请求此时subject.getPrincipal()为null
@RequiresRoles
验证subject是否有相应角色,有角色访问方法,没有则会抛出异常 AuthorizationException。
@RequiresPermissions
验证subject是否有相应权限,有权限访问方法,没有则会抛出异常
AuthorizationException。

授权验证-没有角色无法访问

添加验证角色注解

  1. //登录认证验证角色
  2. @RequiresRoles("admin")
  3. @GetMapping("userLoginRoles")
  4. @ResponseBody
  5. public String userLoginRoles() {
  6. System.out.println("登录认证验证角色");
  7. return "验证角色成功";
  8. }

修改 main.html

  1. Shiro 登录认证后主页面


  2. 登录用户为:"${session.user}">


修改 MyRealm 方法

  1. //自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
  2. @Override
  3. protected AuthorizationInfo
  4. doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  5. System.out.println("进入自定义授权方法");
  6. return null;
  7. }

测试

授权验证-获取角色进行验证  

修改 MyRealm 方法

  1. //自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
  2. @Override
  3. protected AuthorizationInfo
  4. doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  5. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  6. //2 存储角色
  7. info.addRole("admin");
  8. //返回
  9. return info;
  10. }

运行测试

 确认库表

  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='角色表';

  1. CREATE TABLE `role_user` (
  2. `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
  3. `uid` BIGINT(20) DEFAULT NULL COMMENT '用户 id',
  4. `rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
  5. PRIMARY KEY (`id`)
  6. ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色用户映射表';

查询 sql 

根据用户名查询对应角色信息
  1. SELECT NAME FROM role WHERE id IN (SELECT rid FROM role_user WHERE
  2. uid=(SELECT id FROM USER WHERE NAME='张三'));

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 getUserRoleInfoMapper(@Param("principal") String principal);

service 实现

  1. //获取用户的角色信息
  2. @Override
  3. public List getUserRoleInfo(String principal) {
  4. return userMapper.getUserRoleInfoMapper(principal);
  5. }

MyRealm 

  1. //自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
  2. @Override
  3. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  4. System.out.println("进入自定义授权方法");
  5. //获取当前用户身份信息
  6. String principal =
  7. principalCollection.getPrimaryPrincipal().toString();
  8. //调用接口方法获取用户的角色信息
  9. List roles = userService.getUserRoleInfo(principal);
  10. System.out.println("当前用户角色信息:"+roles);
  11. //创建对象,存储当前登录的用户的权限和角色
  12. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  13. //存储角色
  14. info.addRoles(roles);
  15. //返回
  16. return info;
  17. }

配置类

  1. // 防止页面访问多次重定向
  2. @Bean
  3. public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
  4. DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
  5. defaultAdvisorAutoProxyCreator.setUsePrefix(true);
  6. return defaultAdvisorAutoProxyCreator;
  7. }

测试

启动登录测试

授权验证-获取权限进行验证 

获取权限验证和获取角色相类似

确认库表

  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='权限表';

  1. CREATE TABLE `role_ps` (
  2. `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
  3. `rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
  4. `pid` BIGINT(20) DEFAULT NULL COMMENT '权限 id',
  5. PRIMARY KEY (`id`)
  6. ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色权限映射表';

查询 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','userMag')));

mapper 方法

  1. @Select({
  2. ""
  3. })
  4. List getUserPermissionInfoMapper(@Param("roles")List roles);

service 实现

  1. //获取用户角色的权限信息
  2. @Override
  3. public List getUserPermissionInfo(List roles) {
  4. return userMapper.getUserPermissionInfoMapper(roles);
  5. }

MyRealm 方法

  1. //自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
  2. @Override
  3. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  4. System.out.println("进入自定义授权方法");
  5. //获取当前用户身份信息
  6. String principal = principalCollection.getPrimaryPrincipal().toString();
  7. //调用接口方法获取用户的角色信息
  8. List roles = userService.getUserRoleInfo(principal);
  9. System.out.println("当前用户角色信息:"+roles);
  10. //调用接口方法获取用户角色的权限信息
  11. List permissions =
  12. userService.getUserPermissionInfo(roles);
  13. System.out.println("当前用户权限信息:"+permissions);
  14. //创建对象,存储当前登录的用户的权限和角色
  15. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  16. //存储角色
  17. info.addRoles(roles);
  18. //存储权限信息
  19. info.addStringPermissions(permissions);
  20. //返回
  21. return info;
  22. }

添加 controller 方法

  1. //登录认证验证权限
  2. @RequiresPermissions("user:delete")
  3. @GetMapping("userPermissions")
  4. @ResponseBody
  5. public String userLoginPermissions() {
  6. System.out.println("登录认证验证权限");
  7. return "验证权限成功";
  8. }

改造 main.html

  1. Shiro 登录认证后主页面


  2. 登录用户为:"${session.user}">



测试

启动登录测试

授权验证-异常处理 

创建认证异常处理类,使用@ControllerAdvice 加@ExceptionHandler 实现特殊异常处理。
  1. import org.apache.shiro.authz.AuthorizationException;
  2. import org.apache.shiro.authz.UnauthorizedException;
  3. import org.springframework.web.bind.annotation.ControllerAdvice;
  4. import org.springframework.web.bind.annotation.ExceptionHandler;
  5. import org.springframework.web.bind.annotation.ResponseBody;
  6. @ControllerAdvice
  7. public class PermissionsException {
  8. @ResponseBody
  9. @ExceptionHandler(UnauthorizedException.class)
  10. public String unauthorizedException(Exception ex){
  11. return "无权限";
  12. }
  13. @ResponseBody
  14. @ExceptionHandler(AuthorizationException.class)
  15. public String authorizationException(Exception ex){
  16. return "权限认证失败";
  17. }
  18. }

测试

启动运行,用李四登录测试。

前端页面授权验证

引入依赖

  1. com.github.theborakompanioni
  2. thymeleaf-extras-shiro
  3. 2.0.0

配置类添加新配置

用于解析 thymeleaf 中的 shiro: 相关属性
  1. @Bean
  2. public ShiroDialect shiroDialect(){
  3. return new ShiroDialect();
  4. }
T hymeleaf 中常用的 shiro:属性:
guest 标签
用户没有身份验证时显示相应信息,即游客访问信息。
user 标签
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 体内容

改造 main.html

  1. Shiro 登录认证后主页面


  2. 登录用户为:"${session.user}">



测试

  • 相关阅读:
    5分钟自建数据库可视化平台,在线管理数据库也太方便了~
    3.1 OrCAD中怎么创建新的原理图工程文件?OrCAD中原理图的设计纸张大小应该怎么设置?
    react高阶成分(HOC)实践例子
    Redis(一)--Redis入门(2)--Redis数据类型
    linux 系统时间、时区、date、timedatectl
    chapter four in C primer plus
    Linux驱动开发-字符设备驱动开发
    通过requests库使用HTTP编写的爬虫程序
    Java基础面试-IOC
    企业电子期刊怎么做,用这个平台就对啦!
  • 原文地址:https://blog.csdn.net/qq_63431773/article/details/134049321