当前位置:   article > 正文

Shiro 权限处理(自我学习版)_shiro自定义权限验证

shiro自定义权限验证

在做毕设系统的时候想要让我们的系统更加安全,并且看起来更加高大上,使用算法MD5,特学了shiro相关知识,首先什么是Shiro?

官方说法:Apache shiro是一个强大易用的开源安全框架,用于简化应用程序安全和加强其验证处理,以及控制访问控制行为,Apache shiro可以让帮助外面编写易于维护的安全和认证的实现,常用于web以及JavaEE的环境,其实说白了shiro就是一个通过过滤确定具体请求的URL是否允许访问,以及处理认证和授权的方法。

进一步了解shiro,我们可以先明确其基本功能

Authentication:身份认证/登录,验证用户是不是拥有相应的身份

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即 判断用 户是否能进行什么操作

Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web Support:Web 支持,可以非常容易的集成到 Web 环境

Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,可以提高效率

Concurrency:Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

Testing:提供测试支持;

Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

原理: 从外部架构来看

 subject代表当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如机器人,网络爬虫等,与subject的所有交互都会委托给securityManager(安全管理器),subject相当于一个门面,securitymanager才是实际上的执行者,其管理所有的subject,是shiro的核心,他负责和shrio的其他组件进行交互交互,Realm(实际处理认证和授权工作的实体),shiro从Realm获取安全数据如角色,用户,权限,也就是说SecurityManager要验证用户身份,那么他需要从Realm获取相应的用户进行比较来确定用户的身份是否合法,也就是说需要从Realm中得到相应的用户信息进行验证用户能否进行操作

原理: 从内部架构来看

  • securityManager:是shiro的心脏,所有的具体交互都通过securitymanager进行控制,他管理着所有的subject且复杂进行认证、授权、会话及缓存的管理
  • Realm:可以有1个或者多个Realm,可以任务是安全实体数据源,即用于获取安全实体的,可以是JDBC实现,也可以是内存实现等,由用户提供,所以一般应用中都需要实现自己的realm
  • Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密。
  • 登录认证基本流程
    • 收集用户身份/凭证,即用户名/密码调用 Subject.login 进行登录,如果失败将得到相应 的 AuthenticationException异常,根据异常提示用户错误信息;否则登录成功
    • 创建自定义的 Realm 类,继承 org.apache.shiro.realm.AuthenticatingRealm类,实现 doGetAuthenticationInfo() 方法

 具体使用Shiro如何整合到我们SpringBoot项目中

  • 与SpringBoot整合
    • 1.首先IDEA中正常创建springboot项目
    • 2.添加相关依赖pom.xml
      1. <!--shiro-->
      2. <dependency>
      3. <groupId>org.apache.shiro</groupId>
      4. <artifactId>shiro-spring-boot-web-starter</artifactId>
      5. <version>1.9.0</version>
      6. </dependency>
    • 3.给配置文件 application.yml添加基础配置
      1. spring:
      2. datasource:
      3. type: com.zaxxer.hikari.HikariDataSource
      4. driver-class-name: com.mysql.jdbc.Driver
      5. url: jdbc:mysql://127.0.0.1:3306/shirodb?serverTimezone=UTC&characterEncoding=utf-8&useSSL=false
      6. username: root
      7. password: 123456
      8. jackson:
      9. date-format: yyyy-MM-dd HH:mm:ss
      10. time-zone: GMT+8
      11. shiro:
      12. loginUrl: /myController/login
    • 这个地方需要注意的点是看清自己本机的Mysql版本是5.0的还是8.0的,5.0的版本如上述所示,8.0的话加一个cj
      driver-class-name: com.mysql.cj.jdbc.Driver
    • 4.后端接口服务实现
      • 41.创建数据库表shiro-user表,如下所示,我使用的MD5加盐三次加密的形式,其数据库表建立如下(密码为加盐加密后的样式),使用加盐加密的方式更加安全,即使数据丢失也不容易破解,更能保护客户的隐私
      • 42.创建与之相对应的实体entity包 (user)
        1. package com.shiro.entity;
        2. import lombok.AllArgsConstructor;
        3. import lombok.Data;
        4. import lombok.NoArgsConstructor;
        5. @Data
        6. @NoArgsConstructor
        7. @AllArgsConstructor
        8. public class User {
        9. private Integer id;
        10. private String name;
        11. private String pwd;
        12. private Integer rid;
        13. }
      • 4.3创建mapper包  @Repository 数据库仓库继承相应的BaseMapper父类
        1. package com.shiro.mapper;
        2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
        3. import com.shiro.entity.User;
        4. import org.springframework.stereotype.Repository;
        5. @Repository
        6. public interface UserMapper extends BaseMapper<User> {
        7. }
      • 4.4创建service业务层,定义接口UserService,在接口中写登录方法,
        1. package com.shiro.service;
        2. import com.shiro.entity.User;
        3. import com.shiro.mapper.UserMapper;
        4. import org.springframework.beans.factory.annotation.Autowired;
        5. public interface UserService {
        6. //用户登录
        7. User getUserInfoByName(String name);
        8. }
      • 4.5 创建与之相对于的接口实现类UserserviceImpl继承接口UserService,自动装配UserMapper,重写接口方法
        1. package com.shiro.service.impl;
        2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
        3. import com.shiro.entity.User;
        4. import com.shiro.mapper.UserMapper;
        5. import com.shiro.service.UserService;
        6. import org.springframework.beans.factory.annotation.Autowired;
        7. import org.springframework.stereotype.Service;
        8. @Service
        9. public class UserServiceImpl implements UserService {
        10. @Autowired
        11. private UserMapper userMapper;
        12. @Override
        13. public User getUserInfoByName(String name) {
        14. QueryWrapper<User> wrapper=new QueryWrapper<>();
        15. wrapper.eq("name",name);
        16. User user = userMapper.selectOne(wrapper);
        17. return user;
        18. }
        19. }

    • 5. 自定义自己的Realm-MyRealm 继承父类AuthorizingRealm(认证和授权),会重写2个方法,自定义授权方法和自定义登录认证方法,这里自定义登录认证方法是重点,需要获取前端页面输入的用户信息,调取我们业务层userservice从数据库中获取用户信息,判断数据并将其封装返回
      1. package com.shiro.realm;
      2. import com.shiro.entity.User;
      3. import com.shiro.mapper.UserMapper;
      4. import com.shiro.service.UserService;
      5. import org.apache.shiro.authc.AuthenticationException;
      6. import org.apache.shiro.authc.AuthenticationInfo;
      7. import org.apache.shiro.authc.AuthenticationToken;
      8. import org.apache.shiro.authc.SimpleAuthenticationInfo;
      9. import org.apache.shiro.authz.AuthorizationInfo;
      10. import org.apache.shiro.realm.AuthorizingRealm;
      11. import org.apache.shiro.subject.PrincipalCollection;
      12. import org.apache.shiro.util.ByteSource;
      13. import org.springframework.beans.factory.annotation.Autowired;
      14. import org.springframework.stereotype.Component;
      15. @Component
      16. public class MyRealm extends AuthorizingRealm {
      17. @Autowired
      18. private UserService userService;
      19. //自定义授权方法:获取当前登录用户的角色、权限信息,返回给shiro用来进行授权认证
      20. @Override
      21. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
      22. //1.创建对象,封装当前用户登录的角色、权限信息
      23. SimpleAuthenticationInfo info = new SimpleAuthenticationInfo();
      24. //2.存储角色
      25. // info.addRole("admin");
      26. //3.返回信息
      27. return null;
      28. }
      29. //自定义登录认证方法
      30. @Override
      31. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
      32. //1.获取用户身份信息
      33. String name = authenticationToken.getPrincipal().toString();
      34. //2.调用业务层获取用户信息(数据库表中存的)
      35. User user = userService.getUserInfoByName(name);
      36. //3.非空判断将数据封装返回
      37. if (user != null) {
      38. AuthenticationInfo info = new SimpleAuthenticationInfo(
      39. authenticationToken.getPrincipal(),
      40. user.getPwd(),
      41. ByteSource.Util.bytes("salt"),
      42. authenticationToken.getPrincipal().toString()
      43. );
      44. return info;
      45. }
      46. return null;
      47. }
      48. }
    • 6.编写配置类ShiroConfig
      • 6.1 配置SecurityManger(真正的shiro核心管理对象):这里可以根据需求进行加盐加密处理,一般来说只使用MD5加密容易被破解,造成密码丢失信息资料泄露的危害,所以我们可以加盐,所谓加盐就是在密码后面加一段随机的字符串,然后在加密,hashlterations表示加密次数

      • 6.2 配置shiro内置过滤器拦截范围,
        • 其中 authc 表示这个请求需要进行认证(登录),只有认证(登录)通过才能访问,anon表示所有url都可以匿名访问,/ ** 表示任意子路径
          1. package com.shiro.config;
          2. import com.shiro.realm.MyRealm;
          3. import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
          4. import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
          5. import org.apache.shiro.web.mgt.CookieRememberMeManager;
          6. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
          7. import org.apache.shiro.web.servlet.SimpleCookie;
          8. import org.springframework.beans.factory.annotation.Autowired;
          9. import org.springframework.context.annotation.Bean;
          10. import org.springframework.context.annotation.Configuration;
          11. @Configuration
          12. public class ShiroConfig {
          13. @Autowired
          14. private MyRealm myRealm;
          15. // private MyRealm2 myRealm2;
          16. //配置SecurityManager
          17. @Bean
          18. public DefaultWebSecurityManager defaultWebSecurityManager(){
          19. //1 创建 defaultWebSecurityManager 对象
          20. DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
          21. //2 创建加密对象,并设置相关属性
          22. HashedCredentialsMatcher matcher = new 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. //设置remmerberMe
          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 CookieRememberMeManager();
          49. cookieRememberMeManager.setCookie(rememberMeCookie());
          50. cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
          51. return cookieRememberMeManager;
          52. }
          53. //配置 Shiro 内置过滤器拦截范围
          54. @Bean
          55. public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() {
          56. DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
          57. //设置不认证可以访问的资源
          58. definition.addPathDefinition("/myController/userLogin", "anon");
          59. definition.addPathDefinition("/myController/login","anon");
          60. //配置登出过滤器
          61. definition.addPathDefinition("/logout","logout");
          62. //添加存在用户的过滤器(rememberMe)
          63. definition.addPathDefinition("/**","user");
          64. //设置需要进行登录认证的拦截范围
          65. definition.addPathDefinition("/**", "authc");
          66. return definition;
          67. }
          68. }
    • 7.实现controller,创建自己的MyController
      • 请求方法get以及请求路径GetMapper("userLogin"),获取前端用户输入的各种信息subject,封装请求的数据到token对象中,调用login方法进行登录验证,返回给前端验证结果登录成功/登录失败
        1. package com.shiro.controller;
        2. import org.apache.shiro.SecurityUtils;
        3. import org.apache.shiro.authc.AuthenticationException;
        4. import org.apache.shiro.authc.AuthenticationToken;
        5. import org.apache.shiro.authc.UsernamePasswordToken;
        6. import org.apache.shiro.subject.Subject;
        7. import org.springframework.stereotype.Controller;
        8. import org.springframework.web.bind.annotation.*;
        9. import javax.servlet.http.HttpSession;
        10. @Controller
        11. @RequestMapping("myController")
        12. public class MyController {
        13. //跳转登录页面
        14. @GetMapping("login")
        15. public String login() {
        16. return "login";
        17. }
        18. @GetMapping("userLogin")
        19. // @ResponseBody //表达传递数据的意思
        20. public String userLogin(String name, String pwd,@RequestParam(defaultValue = "false")boolean rememberMe, HttpSession session) {
        21. //1.获取subject对象
        22. Subject subject = SecurityUtils.getSubject();
        23. //2.封装请求数据到token
        24. AuthenticationToken token = new UsernamePasswordToken(name, pwd, rememberMe);
        25. //3.调用login方法进行登录认证
        26. try {
        27. subject.login(token);
        28. // return "登录成功";
        29. session.setAttribute("user", token.getPrincipal().toString());
        30. return "main";
        31. } catch (AuthenticationException e) {
        32. e.printStackTrace();
        33. System.out.println("登录失败");
        34. return "登录失败";
        35. }
        36. }
        37. //登录认证验证 rememberMe
        38. @GetMapping("userLoginRm")
        39. public String userLogin(HttpSession session) {
        40. session.setAttribute("user", "rememberMe");
        41. return "main";
        42. }
        43. }
    • 8.测试 -启动服务,通过浏览器进行访问测试
      • shrio测试-get请求,请求信息写在请求体上,浏览器可以直接看见,tomact端口8080,

      • 管理员admin登录,密码为123,带盐的3次加密

    1. <!DOCTYPE html>
    2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Title</title>
    6. </head>
    7. <body>
    8. <h1>Shiro登录认证后主页面</h1>
    9. <br>
    10. 登录用户为:<span th:text="${session.user}"></span>
    11. <br>
    12. <a href="/logout">登出</a>
    13. </body>
    14. </html>
    9.编写简单的前端页面进行测试,登录页面Login.html以及main.html
    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. <form action="/myController/userLogin">
    10. <div> 用户名:<input type="text" name="name" value=""></div>
    11. <div>密码:<input type="password" name="pwd" value=""></div>
    12. <div>记住用户:<input type="checkbox" name="rememberMe" value="true"></div>
    13. <div><input type="submit" value="登录"></div>
    14. </form>
    15. </body>
    16. </html>
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/534019
推荐阅读
相关标签
  

闽ICP备14008679号