服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|JavaScript|易语言|

服务器之家 - 编程语言 - Java教程 - Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

2021-09-07 13:32つ九键按三下 Java教程

这篇文章主要介绍了Java安全框架——Shiro的使用详解,帮助大家更好的理解和学习使用Shiro,感兴趣的朋友可以了解下

Shiro简介

  1. Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理
  2. 三个核心组件:Subject, SecurityManager 和 Realms
  3. Subject代表了当前用户的安全操作
  4. SecurityManager管理所有用户的安全操作,是Shiro框架的核心,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
  5. Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
  6. Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。

Shiro快速入门

导入依赖

  1. <dependency>
  2. <groupId>org.apache.shiro</groupId>
  3. <artifactId>shiro-core</artifactId>
  4. <version>1.7.1</version>
  5. </dependency>
  6.  
  7. <!-- configure logging -->
  8. <!-- https://mvnrepository.com/artifact/org.slf4j/jcl-over-slf4j -->
  9. <dependency>
  10. <groupId>org.slf4j</groupId>
  11. <artifactId>jcl-over-slf4j</artifactId>
  12. <version>2.0.0-alpha1</version>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.slf4j</groupId>
  16. <artifactId>slf4j-log4j12</artifactId>
  17. <version>2.0.0-alpha1</version>
  18. </dependency>
  19. <dependency>
  20. <groupId>log4j</groupId>
  21. <artifactId>log4j</artifactId>
  22. <version>1.2.17</version>
  23. </dependency>

配置log4j.properties

  1. log4j.rootLogger=INFO, stdout
  2.  
  3. log4j.appender.stdout=org.apache.log4j.ConsoleAppender
  4. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
  5. log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
  6.  
  7. # General Apache libraries
  8. log4j.logger.org.apache=WARN
  9.  
  10. # Spring
  11. log4j.logger.org.springframework=WARN
  12.  
  13. # Default Shiro logging
  14. log4j.logger.org.apache.shiro=INFO
  15.  
  16. # Disable verbose logging
  17. log4j.logger.org.apache.shiro.util.ThreadContext=WARN
  18. log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

配置Shiro.ini(在IDEA中需要导入ini插件)

  1. [users]
  2. # user 'root' with password 'secret' and the 'admin' role
  3. root = secret, admin
  4. # user 'guest' with the password 'guest' and the 'guest' role
  5. guest = guest, guest
  6. # user 'presidentskroob' with password '12345' ("That's the same combination on
  7. # my luggage!!!" ;)), and role 'president'
  8. presidentskroob = 12345, president
  9. # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
  10. darkhelmet = ludicrousspeed, darklord, schwartz
  11. # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
  12. lonestarr = vespa, goodguy, schwartz
  13.  
  14. # -----------------------------------------------------------------------------
  15. # Roles with assigned permissions
  16. #
  17. # Each line conforms to the format defined in the
  18. # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
  19. # -----------------------------------------------------------------------------
  20. [roles]
  21. # 'admin' role has all permissions, indicated by the wildcard '*'
  22. admin = *
  23. # The 'schwartz' role can do anything (*) with any lightsaber:
  24. schwartz = lightsaber:*
  25. # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
  26. # license plate 'eagle5' (instance specific id)
  27. goodguy = winnebago:drive:eagle5

快速入门实现类 quickStart.java

  1. import org.apache.shiro.SecurityUtils;
  2. import org.apache.shiro.authc.*;
  3. import org.apache.shiro.config.IniSecurityManagerFactory;
  4. import org.apache.shiro.mgt.DefaultSecurityManager;
  5. import org.apache.shiro.realm.text.IniRealm;
  6. import org.apache.shiro.session.Session;
  7. import org.apache.shiro.subject.Subject;
  8. import org.apache.shiro.util.Factory;
  9. import org.slf4j.Logger;
  10. import org.slf4j.LoggerFactory;
  11.  
  12. public class quickStart {
  13.  
  14. private static final transient Logger log = LoggerFactory.getLogger(quickStart.class);
  15.  
  16. /*
  17. Shiro三大对象:
  18. Subject: 用户
  19. SecurityManager:管理所有用户
  20. Realm: 连接数据
  21. */
  22.  
  23. public static void main(String[] args) {
  24.  
  25. // 创建带有配置的Shiro SecurityManager的最简单方法
  26. // realms, users, roles and permissions 是使用简单的INI配置。
  27. // 我们将使用可以提取.ini文件的工厂来完成此操作,
  28. // 返回一个SecurityManager实例:
  29.  
  30. // 在类路径的根目录下使用shiro.ini文件
  31. // (file:和url:前缀分别从文件和url加载):
  32. //Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
  33. //SecurityManager securityManager = factory.getInstance();
  34. DefaultSecurityManager securityManager = new DefaultSecurityManager();
  35. IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
  36. securityManager.setRealm(iniRealm);
  37.  
  38. // 对于这个简单的示例快速入门,请使SecurityManager
  39. // 可作为JVM单例访问。大多数应用程序都不会这样做
  40. // 而是依靠其容器配置或web.xml进行
  41. // webapps。这超出了此简单快速入门的范围,因此
  42. // 我们只做最低限度的工作,这样您就可以继续感受事物.
  43. SecurityUtils.setSecurityManager(securityManager);
  44.  
  45. // 现在已经建立了一个简单的Shiro环境,让我们看看您可以做什么:
  46.  
  47. // 获取当前用户对象 Subject
  48. Subject currentUser = SecurityUtils.getSubject();
  49.  
  50. // 使用Session做一些事情(不需要Web或EJB容器!!!
  51. Session session = currentUser.getSession();//通过当前用户拿到Session
  52. session.setAttribute("someKey", "aValue");
  53. String value = (String) session.getAttribute("someKey");
  54. if (value.equals("aValue")) {
  55. log.info("Retrieved the correct value! [" + value + "]");
  56. }
  57.  
  58. // 判断当前用户是否被认证
  59. if (!currentUser.isAuthenticated()) {
  60. //token : 令牌,没有获取,随机
  61. UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
  62. token.setRememberMe(true); // 设置记住我
  63. try {
  64. currentUser.login(token);//执行登陆操作
  65. } catch (UnknownAccountException uae) {//打印出 用户名
  66. log.info("There is no user with username of " + token.getPrincipal());
  67. } catch (IncorrectCredentialsException ice) {//打印出 密码
  68. log.info("Password for account " + token.getPrincipal() + " was incorrect!");
  69. } catch (LockedAccountException lae) {
  70. log.info("The account for username " + token.getPrincipal() + " is locked. " +
  71. "Please contact your administrator to unlock it.");
  72. }
  73. // ... 在此处捕获更多异常(也许是针对您的应用程序的自定义异常?
  74. catch (AuthenticationException ae) {
  75. //unexpected condition? error?
  76. }
  77. }
  78.  
  79. //say who they are:
  80. //print their identifying principal (in this case, a username):
  81. log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
  82.  
  83. //test a role:
  84. if (currentUser.hasRole("schwartz")) {
  85. log.info("May the Schwartz be with you!");
  86. } else {
  87. log.info("Hello, mere mortal.");
  88. }
  89.  
  90. //test a typed permission (not instance-level)
  91. if (currentUser.isPermitted("lightsaber:wield")) {
  92. log.info("You may use a lightsaber ring. Use it wisely.");
  93. } else {
  94. log.info("Sorry, lightsaber rings are for schwartz masters only.");
  95. }
  96.  
  97. //a (very powerful) Instance Level permission:
  98. if (currentUser.isPermitted("winnebago:drive:eagle5")) {
  99. log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
  100. "Here are the keys - have fun!");
  101. } else {
  102. log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
  103. }
  104.  
  105. //all done - log out!
  106. currentUser.logout();//注销
  107.  
  108. System.exit(0);//退出
  109. }
  110. }

启动测试

Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

SpringBoot-Shiro整合(最后会附上完整代码)

前期工作

导入shiro-spring整合包依赖

  1. <!-- shiro-spring整合包 -->
  2. <dependency>
  3. <groupId>org.apache.shiro</groupId>
  4. <artifactId>shiro-spring</artifactId>
  5. <version>1.7.1</version>
  6. </dependency>

跳转的页面
index.html

  1. <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
  2. <head>
  3. <meta charset="UTF-8">
  4. <title>首页</title>
  5. </head>
  6. <body>
  7.  
  8. <h1>首页</h1>
  9.  
  10. <p th:text="${msg}"></p>
  11.  
  12. <a th:href="@{/user/add}" rel="external nofollow" rel="external nofollow" rel="external nofollow" >add</a>| <a th:href="@{/user/update}" rel="external nofollow" rel="external nofollow" rel="external nofollow" >update</a>
  13.  
  14. </body>
  15. </html>

add.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>add</title>
  6. </head>
  7. <body>
  8. <p>add</p>
  9. </body>
  10. </html>

update.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>update</title>
  6. </head>
  7. <body>
  8. <p>update</p>
  9. </body>
  10. </html>

编写shiro的配置类ShiroConfig.java

  1. package com.example.config;
  2.  
  3. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
  4. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
  5. import org.springframework.beans.factory.annotation.Qualifier;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8.  
  9. import java.util.LinkedHashMap;
  10. import java.util.Map;
  11.  
  12. @Configuration
  13. public class ShiroConfig {
  14. //3. ShiroFilterFactoryBean
  15. @Bean
  16. public ShiroFilterFactoryBean getshiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
  17. ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
  18. //设置安全管理器
  19. factoryBean.setSecurityManager(defaultWebSecurityManager);
  20.  
  21. return factoryBean;
  22. }
  23.  
  24. //2.创建DefaultWebSecurityManager
  25. @Bean(name = "SecurityManager")
  26. public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
  27. DefaultWebSecurityManager SecurityManager=new DefaultWebSecurityManager();
  28. //3.关联Realm
  29. SecurityManager.setRealm(userRealm);
  30. return SecurityManager;
  31. }
  32. //1.创建Realm对象
  33. @Bean(name = "userRealm")
  34. public UserRealm userRealm(){
  35. return new UserRealm();
  36. }
  37.  
  38. }

编写UserRealm.java

  1. package com.example.config;
  2.  
  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.authz.AuthorizationInfo;
  7. import org.apache.shiro.realm.AuthorizingRealm;
  8. import org.apache.shiro.subject.PrincipalCollection;
  9.  
  10. public class UserRealm extends AuthorizingRealm {
  11.  
  12. @Override
  13. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  14. System.out.println("授权");
  15. return null;
  16. }
  17.  
  18. @Override
  19. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
  20. System.out.println("认证");
  21. return null;
  22. }
  23. }

编写controller测试环境是否搭建好

  1. package com.example.controller;
  2.  
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.ui.Model;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6.  
  7. @Controller
  8. public class MyController {
  9.  
  10. @RequestMapping({"/","/index"})
  11. public String index(Model model){
  12. model.addAttribute("msg","hello,shiro");
  13. return "index";
  14. }
  15.  
  16. @RequestMapping("/user/add")
  17. public String add(){
  18. return "user/add";
  19. }
  20.  
  21. @RequestMapping("/user/update")
  22. public String update(){
  23. return "user/update";
  24. }
  25. }

Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

实现登录拦截

在ShiroConfig.java文件中添加拦截

  1. Map<String,String> filterMap = new LinkedHashMap<>();
  2. //对/user/*下的文件只有拥有authc权限的才能访问
  3. filterMap.put("/user/*","authc");
  4. //将Map存放到ShiroFilterFactoryBean中
  5. factoryBean.setFilterChainDefinitionMap(filterMap);

这样,代码跑起来,你点击add或者update就会出现404错误,这时候,我们再继续添加,让它跳转到我们自定义的登录页

添加登录拦截到登录页

  1. //需进行权限认证时跳转到toLogin
  2. factoryBean.setLoginUrl("/toLogin");
  3. //权限认证失败时跳转到unauthorized
  4. factoryBean.setUnauthorizedUrl("/unauthorized");

login.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>登录</title>
  6. </head>
  7. <body>
  8. <form action="">
  9. 用户名:<input type="text" name="username"><br>
  10. 密码:<input type="text" name="password"><br>
  11. <input type="submit">
  12. </form>
  13.  
  14. </body>
  15. </html>

视图跳转添加一个login页面跳转

  1. @RequestMapping("/toLogin")
  2. public String login(){
  3. return "login";
  4. }

上面,我们已经成功拦截了,现在我们来实现用户认证

首先,我们需要一个登录页面

login.html

  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>登录</title>
  6. </head>
  7. <body>
  8. <p th:text="${msg}" style="color: red"></p>
  9. <form th:action="@{/login}">
  10. 用户名:<input type="text" name="username"><br>
  11. 密码:<input type="text" name="password"><br>
  12. <input type="submit">
  13. </form>
  14.  
  15. </body>
  16. </html>

其次,去controller编写跳转到登录页面

  1. @RequestMapping("/login")
  2. public String login(String username,String password,Model model){
  3. //获得当前的用户
  4. Subject subject = SecurityUtils.getSubject();
  5. //封装用户数据
  6. UsernamePasswordToken taken = new UsernamePasswordToken(username,password);
  7.  
  8. try{//执行登陆操作,没有发生异常就说明登陆成功
  9. subject.login(taken);
  10. return "index";
  11. }catch (UnknownAccountException e){
  12. model.addAttribute("msg","用户名错误");
  13. return "login";
  14. }catch (IncorrectCredentialsException e){
  15. model.addAttribute("msg","密码错误");
  16. return "login";
  17. }
  18.  
  19. }

最后去UserRealm.java配置认证

  1. //认证
  2. @Override
  3. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
  4. System.out.println("认证");
  5.  
  6. String name = "root";
  7. String password = "123456";
  8.  
  9. UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
  10.  
  11. if (!userToken.getUsername().equals(name)){
  12. return null;//抛出异常 用户名错误那个异常
  13. }
  14.  
  15. //密码认证,shiro自己做
  16. return new SimpleAuthenticationInfo("",password,"");
  17. }

运行测试,成功!!!

附上最后的完整代码

pom.xml引入的依赖

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.4.4</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.example</groupId>
  12. <artifactId>springboot-08-shiro</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>springboot-08-shiro</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20.  
  21. <!-- shiro-spring整合包 -->
  22. <dependency>
  23. <groupId>org.apache.shiro</groupId>
  24. <artifactId>shiro-spring</artifactId>
  25. <version>1.7.1</version>
  26. </dependency>
  27.  
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-web</artifactId>
  35. </dependency>
  36.  
  37. <dependency>
  38. <groupId>org.springframework.boot</groupId>
  39. <artifactId>spring-boot-starter-test</artifactId>
  40. <scope>test</scope>
  41. </dependency>
  42. </dependencies>
  43.  
  44. <build>
  45. <plugins>
  46. <plugin>
  47. <groupId>org.springframework.boot</groupId>
  48. <artifactId>spring-boot-maven-plugin</artifactId>
  49. </plugin>
  50. </plugins>
  51. </build>
  52.  
  53. </project>

静态资源

index.html

  1. <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
  2. <head>
  3. <meta charset="UTF-8">
  4. <title>首页</title>
  5. </head>
  6. <body>
  7.  
  8. <h1>首页</h1>
  9.  
  10. <p th:text="${msg}"></p>
  11.  
  12. <a th:href="@{/user/add}" rel="external nofollow" rel="external nofollow" rel="external nofollow" >add</a>| <a th:href="@{/user/update}" rel="external nofollow" rel="external nofollow" rel="external nofollow" >update</a>
  13.  
  14. </body>
  15. </html>

login.html

  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>登录</title>
  6. </head>
  7. <body>
  8. <p th:text="${msg}" style="color: red"></p>
  9. <form th:action="@{/login}">
  10. 用户名:<input type="text" name="username"><br>
  11. 密码:<input type="text" name="password"><br>
  12. <input type="submit">
  13. </form>
  14.  
  15. </body>
  16. </html>

add.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>add</title>
  6. </head>
  7. <body>
  8. <p>add</p>
  9. </body>
  10. </html>

update.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>update</title>
  6. </head>
  7. <body>
  8. <p>update</p>
  9. </body>
  10. </html>

controller层

MyController.java

  1. package com.example.controller;
  2.  
  3. import org.apache.shiro.SecurityUtils;
  4. import org.apache.shiro.authc.IncorrectCredentialsException;
  5. import org.apache.shiro.authc.UnknownAccountException;
  6. import org.apache.shiro.authc.UsernamePasswordToken;
  7. import org.apache.shiro.subject.Subject;
  8. import org.springframework.stereotype.Controller;
  9. import org.springframework.ui.Model;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11.  
  12. @Controller
  13. public class MyController {
  14.  
  15. @RequestMapping({"/","/index"})
  16. public String index(Model model){
  17. model.addAttribute("msg","hello,shiro");
  18. return "index";
  19. }
  20.  
  21. @RequestMapping("/user/add")
  22. public String add(){
  23. return "user/add";
  24. }
  25.  
  26. @RequestMapping("/user/update")
  27. public String update(){
  28. return "user/update";
  29. }
  30.  
  31. @RequestMapping("/toLogin")
  32. public String toLogin(){
  33. return "login";
  34. }
  35.  
  36. @RequestMapping("/login")
  37. public String login(String username,String password,Model model){
  38. //获得当前的用户
  39. Subject subject = SecurityUtils.getSubject();
  40. //封装用户数据
  41. UsernamePasswordToken taken = new UsernamePasswordToken(username,password);
  42.  
  43. try{//执行登陆操作,没有发生异常就说明登陆成功
  44. subject.login(taken);
  45. return "index";
  46. }catch (UnknownAccountException e){
  47. model.addAttribute("msg","用户名错误");
  48. return "login";
  49. }catch (IncorrectCredentialsException e){
  50. model.addAttribute("msg","密码错误");
  51. return "login";
  52. }
  53.  
  54. }
  55.  
  56. }

config文件

ShiroConfig.java

  1. package com.example.config;
  2.  
  3. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
  4. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
  5. import org.springframework.beans.factory.annotation.Qualifier;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8.  
  9. import java.util.LinkedHashMap;
  10. import java.util.Map;
  11.  
  12. @Configuration
  13. public class ShiroConfig {
  14. //4. ShiroFilterFactoryBean
  15. @Bean
  16. public ShiroFilterFactoryBean getshiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
  17. ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
  18. //5. 设置安全管理器
  19. factoryBean.setSecurityManager(defaultWebSecurityManager);
  20.  
  21. /* shiro内置过滤器
  22. anon 无需授权、登录就可以访问,所有人可访。
  23. authc 需要登录授权才能访问。
  24. authcBasic Basic HTTP身份验证拦截器
  25. logout 退出拦截器。退出成功后,会 redirect到设置的/URI
  26. noSessionCreation 不创建会话连接器
  27. perms 授权拦截器,拥有对某个资源的权限才可访问
  28. port 端口拦截器
  29. rest rest风格拦截器
  30. roles 角色拦截器,拥有某个角色的权限才可访问
  31. ssl ssl拦截器。通过https协议才能通过
  32. user 用户拦截器,需要有remember me功能方可使用
  33. */
  34. Map<String,String> filterMap = new LinkedHashMap<>();
  35. //对/user/*下的文件只有拥有authc权限的才能访问
  36. filterMap.put("/user/*","authc");
  37. //将Map存放到ShiroFilterFactoryBean中
  38. factoryBean.setFilterChainDefinitionMap(filterMap);
  39. //需进行权限认证时跳转到toLogin
  40. factoryBean.setLoginUrl("/toLogin");
  41. //权限认证失败时跳转到unauthorized
  42. factoryBean.setUnauthorizedUrl("/unauthorized");
  43.  
  44. return factoryBean;
  45. }
  46.  
  47. //2.创建DefaultWebSecurityManager
  48. @Bean(name = "SecurityManager")
  49. public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
  50. DefaultWebSecurityManager SecurityManager=new DefaultWebSecurityManager();
  51. //3.关联Realm
  52. SecurityManager.setRealm(userRealm);
  53. return SecurityManager;
  54. }
  55. //1.创建Realm对象
  56. @Bean(name = "userRealm")
  57. public UserRealm userRealm(){
  58. return new UserRealm();
  59. }
  60.  
  61. }

UserRealm.java

  1. package com.example.config;
  2.  
  3. import org.apache.shiro.authc.*;
  4. import org.apache.shiro.authz.AuthorizationInfo;
  5. import org.apache.shiro.realm.AuthorizingRealm;
  6. import org.apache.shiro.subject.PrincipalCollection;
  7.  
  8. public class UserRealm extends AuthorizingRealm {
  9.  
  10. //授权
  11. @Override
  12. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  13. System.out.println("授权");
  14. return null;
  15. }
  16. //认证
  17. @Override
  18. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
  19. System.out.println("认证");
  20.  
  21. String name = "root";
  22. String password = "123456";
  23.  
  24. UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
  25.  
  26. if (!userToken.getUsername().equals(name)){
  27. return null;//抛出异常 用户名错误那个异常
  28. }
  29.  
  30. //密码认证,shiro自己做
  31. return new SimpleAuthenticationInfo("",password,"");
  32. }
  33. }

但是,我们在用户认证这里,真实情况是从数据库中取的,所以,我们接下来去实现一下从数据库中取出数据来实现用户认证

Shiro整合mybatis

前期工作

在前面导入的依赖中,继续添加以下依赖

  1. <!-- mysql -->
  2. <dependency>
  3. <groupId>mysql</groupId>
  4. <artifactId>mysql-connector-java</artifactId>
  5. </dependency>
  6. <!-- log4j -->
  7. <dependency>
  8. <groupId>log4j</groupId>
  9. <artifactId>log4j</artifactId>
  10. <version>1.2.17</version>
  11. </dependency>
  12. <!-- 数据源Druid -->
  13. <dependency>
  14. <groupId>com.alibaba</groupId>
  15. <artifactId>druid</artifactId>
  16. <version>1.2.5</version>
  17. </dependency>
  18. <!-- 引入mybatis -->
  19. <dependency>
  20. <groupId>org.mybatis.spring.boot</groupId>
  21. <artifactId>mybatis-spring-boot-starter</artifactId>
  22. <version>2.1.4</version>
  23. </dependency>
  24. <!-- lombok -->
  25. <dependency>
  26. <groupId>org.projectlombok</groupId>
  27. <artifactId>lombok</artifactId>
  28. </dependency>

导入了mybatis和Druid,就去application.properties配置一下和Druid
Druid

  1. spring:
  2. datasource:
  3. username: root
  4. password: 123456
  5. url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
  6. driver-class-name: com.mysql.cj.jdbc.Driver
  7. type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源
  8.  
  9. #Spring Boot 默认是不注入这些属性值的,需要自己绑定
  10. #druid 数据源专有配置
  11. initialSize: 5
  12. minIdle: 5
  13. maxActive: 20
  14. maxWait: 60000
  15. timeBetweenEvictionRunsMillis: 60000
  16. minEvictableIdleTimeMillis: 300000
  17. validationQuery: SELECT 1 FROM DUAL
  18. testWhileIdle: true
  19. testOnBorrow: false
  20. testOnReturn: false
  21. poolPreparedStatements: true
  22.  
  23. #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
  24. #如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
  25. #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
  26. filters: stat,wall,log4j
  27. maxPoolPreparedStatementPerConnectionSize: 20
  28. useGlobalDataSourceStat: true
  29. connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

mybatis

  1. mybatis:
  2. type-aliases-package: com.example.pojo
  3. mapper-locations: classpath:mapper/*.xml

连接数据库
编写实体类

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

编写mapper

  1. package com.example.mapper;
  2.  
  3. import com.example.pojo.User;
  4. import org.apache.ibatis.annotations.Mapper;
  5. import org.springframework.stereotype.Repository;
  6.  
  7. @Repository
  8. @Mapper
  9. public interface UserMapper {
  10. public User getUserByName(String name);
  11. }

编写mapper.xml

  1. <?xml version="1.0" encoding="UTF8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5.  
  6. <mapper namespace="com.example.mapper.UserMapper">
  7.  
  8. <select id="getUserByName" parameterType="String" resultType="User">
  9. select * from mybatis.user where name=#{name}
  10. </select>
  11.  
  12. </mapper>

编写service

  1. package com.example.service;
  2.  
  3. import com.example.pojo.User;
  4.  
  5. public interface UserService {
  6. public User getUserByName(String name);
  7. }
  1. package com.example.service;
  2.  
  3. import com.example.mapper.UserMapper;
  4. import com.example.pojo.User;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Service;
  7.  
  8. @Service
  9. public class UserServiceImpl implements UserService{
  10.  
  11. @Autowired
  12. UserMapper userMapper;
  13.  
  14. @Override
  15. public User getUserByName(String name) {
  16. return userMapper.getUserByName(name);
  17. }
  18. }

使用数据库中的数据

修改UserRealm.java即可

  1. package com.example.config;
  2.  
  3. import com.example.pojo.User;
  4. import com.example.service.UserService;
  5. import org.apache.shiro.authc.*;
  6. import org.apache.shiro.authz.AuthorizationInfo;
  7. import org.apache.shiro.realm.AuthorizingRealm;
  8. import org.apache.shiro.subject.PrincipalCollection;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10.  
  11. public class UserRealm extends AuthorizingRealm {
  12.  
  13. @Autowired
  14. UserService userService;
  15.  
  16. //授权
  17. @Override
  18. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  19. System.out.println("授权");
  20. return null;
  21. }
  22. //认证
  23. @Override
  24. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
  25. System.out.println("认证");
  26.  
  27. UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
  28.  
  29. //连接真实的数据库
  30. User user = userService.getUserByName(userToken.getUsername());
  31.  
  32. if (user==null){
  33. return null;//抛出异常 用户名错误那个异常
  34. }
  35.  
  36. //密码认证,shiro自己做
  37. return new SimpleAuthenticationInfo("",user.getPwd(),"");
  38. }
  39. }

认证搞完了,我们再来看看授权

在ShiroConfig.java文件加入授权,加入这行代码: filterMap.put("/user/add","perms[user:add]");//只有拥有user:add权限的人才能访问add,注意授权的位置在认证前面,不然授权会认证不了;

Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

运行测试:add页面无法访问

Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

授权同理:filterMap.put("/user/update","perms[user:update]");//只有拥有user:update权限的人才能访问update

自定义一个未授权跳转页面

在ShiroConfig.java文件设置未授权时跳转到unauthorized页面,加入这行代码:
factoryBean.setUnauthorizedUrl("/unauthorized"); 2. 去Mycontroller写跳转未授权页面

  1. @RequestMapping("/unauthorized")
  2. @ResponseBody//懒得写界面,返回一个字符串
  3. public String unauthorized(){
  4. return "没有授权,无法访问";
  5. }

运行效果:

Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

从数据库中接受用户的权限,进行判断

在数据库中添加一个属性perms,相应的实体类也要修改

Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

修改UserRealm.java

  1. package com.example.config;
  2.  
  3. import com.example.pojo.User;
  4. import com.example.service.UserService;
  5. import org.apache.shiro.SecurityUtils;
  6. import org.apache.shiro.authc.*;
  7. import org.apache.shiro.authz.AuthorizationInfo;
  8. import org.apache.shiro.authz.SimpleAuthorizationInfo;
  9. import org.apache.shiro.realm.AuthorizingRealm;
  10. import org.apache.shiro.subject.PrincipalCollection;
  11. import org.apache.shiro.subject.Subject;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13.  
  14. public class UserRealm extends AuthorizingRealm {
  15.  
  16. @Autowired
  17. UserService userService;
  18.  
  19. //授权
  20. @Override
  21. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  22. System.out.println("授权");
  23. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  24.  
  25. //没有使用数据库,直接自己设置的用户权限,给每个人都设置了,现实中要从数据库中取
  26. //info.addStringPermission("user:add");
  27.  
  28. //从数据库中得到权限信息
  29. //获得当前登录的对象
  30. Subject subject = SecurityUtils.getSubject();
  31. //拿到User对象,通过getPrincipal()获得
  32. User currentUser = (User) subject.getPrincipal();
  33.  
  34. //设置当前用户的权限
  35. info.addStringPermission(currentUser.getPerms());
  36.  
  37. return info;
  38. }
  39. //认证
  40. @Override
  41. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
  42. System.out.println("认证");
  43.  
  44. UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
  45.  
  46. //连接真实的数据库
  47. User user = userService.getUserByName(userToken.getUsername());
  48.  
  49. if (user==null){
  50. return null;//抛出异常 用户名错误那个异常
  51. }
  52.  
  53. //密码认证,shiro自己做
  54. return new SimpleAuthenticationInfo(user,user.getPwd(),"");
  55. }
  56. }

Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

有了授权后,就又出现了一个问题,我们是不是要让用户没有权限的东西,就看不见呢?这时候,就出现了Shiro-thymeleaf整合

Shiro-thymeleaf整合

导入整合的依赖

  1. <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
  2. <dependency>
  3. <groupId>com.github.theborakompanioni</groupId>
  4. <artifactId>thymeleaf-extras-shiro</artifactId>
  5. <version>2.0.0</version>
  6. </dependency>

在ShiroConfig整合ShiroDialect

  1. //整合ShiroDialect: 用来整合 shiro thymeleaf
  2. @Bean
  3. public ShiroDialect getShiroDialect(){
  4. return new ShiroDialect();
  5. }

修改index页面

  1. <html lang="en" xmlns:th="http://www.thymeleaf.org"
  2. xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
  3. <!-- 三个命名空间
  4. xmlns:th="http://www.thymeleaf.org"
  5. xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
  6. xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
  7. -->
  8. <head>
  9. <meta charset="UTF-8">
  10. <title>首页</title>
  11. </head>
  12. <body>
  13.  
  14. <h1>首页</h1>
  15.  
  16. <p th:text="${msg}"></p>
  17.  
  18. <!--判断是否有用户登录,如果有就不显示登录按钮-->
  19. <div th:if="${session.loginUser==null}">
  20. <a th:href="@{/toLogin}" rel="external nofollow" >登录</a>
  21. </div>
  22.  
  23. <div shiro:hasPermission="user:add">
  24. <a th:href="@{/user/add}" rel="external nofollow" rel="external nofollow" rel="external nofollow" >add</a>
  25. </div>
  26. <div shiro:hasPermission="user:update">
  27. <a th:href="@{/user/update}" rel="external nofollow" rel="external nofollow" rel="external nofollow" >update</a>
  28. </div>
  29.  
  30. </body>
  31. </html>

判断是否有用户登录

  1. //这个是整合shiro和thymeleaf用到的,让登录按钮消失的判断
  2. Subject subject = SecurityUtils.getSubject();
  3. Session session = subject.getSession();
  4. session.setAttribute("loginUser", user);

测试

以上就是Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)的详细内容,更多关于Java安全框架——Shiro的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/6949793422483914789

延伸 · 阅读

精彩推荐