Shiro
Shiro是一个安全框架,用于认证、授权和管理应用程序的安全性。它提供了一组易于使用的API和工具,可以帮助您轻松地添加安全性到您的应用程序中;保护应用程序的机密性、完整性和可用性。可以与各种应用程序集成,包括Web应用程序、RESTful服务和基于消息的应用程序等。使用Shiro,您可以轻松地实现身份验证、权限控制、密码加密等功能,以确保您的应用程序得到充分的安全保护;
Security 和 Shiro
Spring Security和Shiro都是用于应用程序安全的框架,它们都提供了认证、授权和安全管理等方面的功能,但它们在实现方式和设计哲学上有一些不同。
- 来源不同:Spring Security最初是Spring框架的一个子项目,而Shiro是从Apache的一个开源项目而来
- 架构不同:Spring Security的设计哲学是将安全性集成到应用程序的架构中,这意味着Spring Security在许多方面都与Spring框架紧密耦合。而Shiro的设计哲学是通过简单的API和注解来实现安全性,使它可以与各种框架和技术集成
- 功能不同:尽管Spring Security和Shiro都提供了基本的认证和授权功能,但它们的实现方式和可定制性略有不同。Spring Security具有更多的配置选项和扩展性,而Shiro则更加简单易用,但可能在某些方面缺少灵活性
- 社区支持不同:由于Spring Security是Spring框架的一部分,因此它拥有强大的社区支持和生态系统。而Shiro虽然也有不错的社区支持,但在某些方面可能不如Spring Security流行
综上所述,选择Spring Security还是Shiro取决于您的具体需求和技术偏好。如果您已经使用了Spring框架,那么Spring Security可能是更好的选择。如果您需要一个更加简单易用的框架,并且需要与各种技术集成,那么Shiro可能更适合您的需求
Authentication:保证只有具有权限的用户才能访问系统中的特定资源,比如用户名/密码、敏感资源等。这样可以保护系统的安全性,防止未经授权的用户访问重要信息;
Authorization:作用在于根据用户提供的身份凭证,生成权限实体,并为之授予相应的权限;
Session Management:会话管理,Session 管理的作用主要是在网站浏览时保存用户的会话状态,当用户关闭浏览器时自动关闭会话,从而避免数据泄露;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web 支持,可以非常容易的集成到 Web 环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率;
Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,通过session缓存数据
Subject(主体) :Shiro 框架中的一个核心概念获取当前登录的用户名和角色,验证当前用户的权限,提供当前用户信息,包括用户名、角色、权限等信息
SecurityManager(核心安全管理器): 是一个安全管理器,主要对账号、权限及身份认证进行设置和管理。它可以对 Spark 的部署模式进行配置,开放指定的权限,没有配置的权限就认为不具备相应的权限,这个安全管理器默认情况下是关闭的,需要手动去开启
Realm(领域) : Shiro 框架中用于保护应用程序中的数据安全的一种数据库;当用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的 Realm 中查找用户及其权限信息。Realm 实质上是一个安全相关的 DAO,它封装了数据源的连接细节,并在需要时将相关数据提供给 Shiro。当配置 Shiro 时,你必须至少指定一个 Realm,用于认证和(或)授权。配置多个 Realm 是可以的,但是至少需要一个。Shiro 内置了可以连接大量安全数据源(又名目录)的 Realm,如 LDAP、关系数据库(JDBC)、类似 INI 的文本配置资源以及属性文件等。如果缺省的 Realm 不能满足需求,你还可以插入代表自定义数据源的自己的 Realm 实现。Realm 能做的工作主要有以下几个方面:
- 身份验证:验证账户和密码,并返回相关信息
- 权限获取:获取指定身份的权限,并返回相关信息
- 令牌支持:判断该令牌(Token)是否被支持
- 令牌有很多种类型,例如:HostAuthenticationToken(主机验证令牌),UsernamePasswordToken(账户密码验证令牌)
准备工作
- 依赖导入
<dependencies> <!--shiro-core--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.11.0</version> </dependency> <!--configure logging--> <!--jcl-over-slf4j--> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>2.0.7</version> </dependency> <!--slf4j-log4j12--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>2.0.7</version> </dependency> <!--log4j--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
- resources/log4j.properties
log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n # General Apache libraries log4j.logger.org.apache=WARN # Spring log4j.logger.org.springframework=WARN # Default Shiro logging log4j.logger.org.apache.shiro=INFO # Disable verbose logging log4j.logger.org.apache.shiro.util.ThreadContext=WARN log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
- resources/shiro.ini
[users] # user 'root' with password 'secret' and the 'admin' role root = secret, admin # user 'guest' with the password 'guest' and the 'guest' role guest = guest, guest # user 'presidentskroob' with password '12345' ("That's the same combination on # my luggage!!!" ;)), and role 'president' presidentskroob = 12345, president # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz' darkhelmet = ludicrousspeed, darklord, schwartz # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz' lonestarr = vespa, goodguy, schwartz [roles] # 'admin' role has all permissions, indicated by the wildcard '*' admin = * # The 'schwartz' role can do anything (*) with any lightsaber: schwartz = lightsaber:* # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with # license plate 'eagle5' (instance specific id) goodguy = winnebago:drive:eagle5
- Quickstart.java
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Simple Quickstart application showing how to use Shiro's API. * * @since 0.9 RC2 */ public class Quickstart { private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class); public static void main(String[] args) { // The easiest way to create a Shiro SecurityManager with configured // realms, users, roles and permissions is to use the simple INI config. // We'll do that by using a factory that can ingest a .ini file and // return a SecurityManager instance: // Use the shiro.ini file at the root of the classpath // (file: and url: prefixes load from files and urls respectively): Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); // for this simple example quickstart, make the SecurityManager // accessible as a JVM singleton. Most applications wouldn't do this // and instead rely on their container configuration or web.xml for // webapps. That is outside the scope of this simple quickstart, so // we'll just do the bare minimum so you can continue to get a feel // for things. SecurityUtils.setSecurityManager(securityManager); // Now that a simple Shiro environment is set up, let's see what you can do: // get the currently executing user: Subject currentUser = SecurityUtils.getSubject(); // Do some stuff with a Session (no need for a web or EJB container!!!) Session session = currentUser.getSession(); session.setAttribute("someKey", "aValue"); String value = (String) session.getAttribute("someKey"); if (value.equals("aValue")) { log.info("Retrieved the correct value! [" + value + "]"); } // let's login the current user so we can check against roles and permissions: if (!currentUser.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); token.setRememberMe(true); try { currentUser.login(token); } catch (UnknownAccountException uae) { log.info("There is no user with username of " + token.getPrincipal()); } catch (IncorrectCredentialsException ice) { log.info("Password for account " + token.getPrincipal() + " was incorrect!"); } catch (LockedAccountException lae) { log.info("The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it."); } // ... catch more exceptions here (maybe custom ones specific to your application? catch (AuthenticationException ae) { //unexpected condition? error? } } //say who they are: //print their identifying principal (in this case, a username): log.info("User [" + currentUser.getPrincipal() + "] logged in successfully."); //test a role: if (currentUser.hasRole("schwartz")) { log.info("May the Schwartz be with you!"); } else { log.info("Hello, mere mortal."); } //test a typed permission (not instance-level) if (currentUser.isPermitted("lightsaber:wield")) { log.info("You may use a lightsaber ring. Use it wisely."); } else { log.info("Sorry, lightsaber rings are for schwartz masters only."); } //a (very powerful) Instance Level permission: if (currentUser.isPermitted("winnebago:drive:eagle5")) { log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " + "Here are the keys - have fun!"); } else { log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!"); } //all done - log out! currentUser.logout(); System.exit(0); } }
shiro的subject分析
- 获取当前用户对象subject
Subject currentUser = SecurityUtils.getSubject();
- 通过当前用户,取出session
Session session = currentUser.getSession();
- 判断当前用户是否被认证
if (!currentUser.isAuthenticated()) { …… }
- 输出Subject信息,或得当前用户认证
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
- 获取用户是否拥有角色,并输出
if (currentUser.hasRole("schwartz")) { …… }
- 获取当前用户权限
if (currentUser.isPermitted("lightsaber:wield")) { …… }
- 注销、结束
//注销 currentUser.logout(); //结束 System.exit(0);
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Quickstart { private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class); public static void main(String[] args) { Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); //获取当前用户对象 Subject currentUser = SecurityUtils.getSubject(); //通过当前用户,取出session Session session = currentUser.getSession(); session.setAttribute("someKey", "aValue"); String value = (String) session.getAttribute("someKey"); if (value.equals("aValue")) { log.info("Retrieved the correct value! [" + value + "]"); } //判断当前用户是否被认证 if (!currentUser.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); //token:令牌 token.setRememberMe(true); try { //执行登录操作 currentUser.login(token); //Subject异常 } catch (UnknownAccountException uae) { log.info("There is no user with username of " + token.getPrincipal()); //令牌不对应等 } catch (IncorrectCredentialsException ice) { log.info("Password for account " + token.getPrincipal() + " was incorrect!"); //账号被锁定问题,登陆次数过多 } catch (LockedAccountException lae) { log.info("The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it."); } //其他异常或者大的异常,自定义异常 catch (AuthenticationException ae) { } } //输出Subject信息,或得当前用户认证 log.info("User [" + currentUser.getPrincipal() + "] logged in successfully."); //测试角色,如果角色拥有权限,则输出权限信息 if (currentUser.hasRole("schwartz")) { log.info("May the Schwartz be with you!"); } else { log.info("Hello, mere mortal."); } //粗粒度 if (currentUser.isPermitted("lightsaber:wield")) { log.info("You may use a lightsaber ring. Use it wisely."); } else { log.info("Sorry, lightsaber rings are for schwartz masters only."); } //细粒度 if (currentUser.isPermitted("winnebago:drive:eagle5")) { log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " + "Here are the keys - have fun!"); } else { log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!"); } //注销 currentUser.logout(); //结束 System.exit(0); } }
springboot集成shiro
- 依赖导入
<dependencies> <!--shiro-springboot--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.11.0</version> </dependency> <!--thymeleaf--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--test--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
- Shiro 框架的配置文件(ShiroConfig)
package com.wei.config; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; @Configuration public class ShiroConfig { //ShiroFilterFactoryBean @Bean(name = "shiroFilterFactoryBean") public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(defaultWebSecurityManager); //添加shiro的内置过滤器 /** * anon:无需认证,即可访问 * authc:必须认证,才能访问 * user:必须拥有rember me 功能才能使用 * perms:必须对某个资源的权限才能访问 * role:拥有某个角色权限才能访问 * */ //登录拦截器 Map<String, String> filterMap = new LinkedHashMap<>(); //filterMap.put("/user/*","authc"); filterMap.put("/user/add","authc"); filterMap.put("/user/update","authc"); bean.setFilterChainDefinitionMap(filterMap); //设置登录请求 bean.setLoginUrl("/toLogin"); return bean; } //DefaultWebSecurityManager @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //关联UserRealm securityManager.setRealm(userRealm); return securityManager; } //Realm对象 @Bean public UserRealm userRealm(){ return new UserRealm(); } }
- login.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>登录</h1> <p th:text="${msg}" style="color: red"></p> <form th:action="@{/login}"> <p>用户名:<input type="text" name="username"></p> <p>密码:<input type="text" name="password"></p> <p><input type="submit"></p> </form> </body> </html>
- index.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>wei_shuo</title> </head> <body> <div> <span><h1>首页</h1></span> <p th:text="${msg}"></p> <hr> <a th:href="@{/user/add}"><h1>add</h1></a> <a th:href="@{/user/update}"><h1>update</h1></a> </div> </body> </html>
- add.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>add User</h1> </body> </html>
- update.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>update User</h1> </body> </html>
- MyController.java
package com.wei.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class MyController { @RequestMapping({"/", "index"}) public String toIndex(Model model) { model.addAttribute("msg", "Hello,Shiro"); return "index"; } @RequestMapping("/user/add") public String add() { return "user/add.html"; } @RequestMapping("/user/update") public String update() { return "user/update.html"; } @RequestMapping("/toLogin") public String toLogin() { return "login"; } @RequestMapping("/login") public String login(String username, String password, Model model) { //获取当前的用户 Subject subject = SecurityUtils.getSubject(); //封装用户的登录数据 UsernamePasswordToken token = new UsernamePasswordToken(username, password); //执行登录方法,如果无异常则成功 try { subject.login(token); return "index"; } catch (UnknownAccountException e) { //用户名不存在 model.addAttribute("msg", "用户名错误"); return "login"; } catch (IncorrectCredentialsException e) { //密码不存在 model.addAttribute("msg", "密码错误"); return "login"; } } }
- UserRealm.java(用户认证与授权)
package com.wei.config; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; //自定义UserRealm public class UserRealm extends AuthorizingRealm { //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行了=>授权doGetAuthorizationInfo"); return null; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行了=>认证doGetAuthorizationInfo"); //用户名,密码,数据库 String name = "root"; String password = "123456"; UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; //用户认证 if (!userToken.getUsername().equals(name)){ return null; //抛出异常 UnknownAccountException } //密码认证,shiro自动部署 return new SimpleAuthenticationInfo("",password,""); } }
shiro集成Mybatis
- 依赖导入
<dependencies> <!--springboot-mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.0</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.26</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--log4j--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.16</version> </dependency> <!--shiro-springboot--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.11.0</version> </dependency> <!--thymeleaf--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--test--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
- 配置文件application.properties
#整合mybatis mybatis.type-aliases-package=com.wei.pojo mybatis.mapper-locations=classpath:mapper/*.xml
- 数据库配置application.yml
spring: datasource: username: "root" password: "root" url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC driver-class-name: com.mysql.cj.jdbc.Driver #切换数据源 type: com.alibaba.druid.pool.DruidDataSource #Spring Boot 默认是不注入这些属性值的,需要自己绑定 #druid 数据源专有配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入 #如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
- /pojo/User.java
package com.wei.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String pwd; }
- /mapper/UserMapper.java
package com.wei.mapper; import com.wei.pojo.User; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; @Repository @Mapper public interface UserMapper { public User queryUserByName(String name); }
- /resource/mapper/UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.wei.mapper.UserMapper"> <select id="queryUserByName" parameterType="String" resultType="User"> select * from mybatis.user where name = #{name} </select> </mapper>
- /service/UserService.java
package com.wei.service; import com.wei.pojo.User; public interface UserService { public User queryUserByName(String name); }
- /service/UserServiceImpl.java
package com.wei; import com.wei.service.UserServiceImpl; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class Springboot06ShiroApplicationTests { @Autowired UserServiceImpl userService; @Test void contextLoads() { System.out.println(userService.queryUserByName("aaa")); } }
- 测试
package com.wei; import com.wei.service.UserServiceImpl; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class Springboot06ShiroApplicationTests { @Autowired UserServiceImpl userService; @Test void contextLoads() { System.out.println(userService.queryUserByName("aaa")); } }
- /config/UserRealm.java
package com.wei.config; import com.wei.pojo.User; import com.wei.service.UserService; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; //自定义UserRealm public class UserRealm extends AuthorizingRealm { @Autowired UserService userService; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行了=>授权doGetAuthorizationInfo"); return null; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行了=>认证doGetAuthorizationInfo"); UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; //连接数据库 User user = userService.queryUserByName(userToken.getUsername()); if (user==null){ return null; //UnknownAccountException } //密码认证,shiro自动部署 //密码加密 return new SimpleAuthenticationInfo("",user.getPwd(),""); } }
用户认证与授权
- 数据库配置
- config/ShiroConfig.java
package com.wei.config; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; @Configuration public class ShiroConfig { //ShiroFilterFactoryBean @Bean("shiroFilterFactoryBean") public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(defaultWebSecurityManager); //添加shiro的内置过滤器 /** * anon:无需认证,即可访问 * authc:必须认证,才能访问 * user:必须拥有rember me 功能才能使用 * perms:必须对某个资源的权限才能访问 * role:拥有某个角色权限才能访问 * */ //登录拦截器 Map<String, String> filterMap = new LinkedHashMap<>(); //filterMap.put("/user/*","authc"); filterMap.put("/user/add","authc"); filterMap.put("/user/update","authc"); bean.setFilterChainDefinitionMap(filterMap); //授权 filterMap.put("/user/add","perms[user:add]"); filterMap.put("/user/update","perms[user:update]"); //设置登录请求 bean.setLoginUrl("/toLogin"); //设置未授权的请求 bean.setUnauthorizedUrl("/noauth"); return bean; } //DefaultWebSecurityManager @Bean("SecurityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //关联UserRealm securityManager.setRealm(userRealm); return securityManager; } //Realm对象 @Bean public UserRealm userRealm(){ return new UserRealm(); } }
config/UserRealm.java
package com.wei.config; import com.wei.pojo.User; import com.wei.service.UserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; //自定义UserRealm public class UserRealm extends AuthorizingRealm { @Autowired UserService userService; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行了=>授权doGetAuthorizationInfo"); //SimpleAuthorizationInfo SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // info.addStringPermission("user:add"); // info.addStringPermission("user:update"); //获取当前用户对象 Subject subject = SecurityUtils.getSubject(); //获取到了user对象 User currentUser = (User) subject.getPrincipal(); //设置当前用户权限 info.addStringPermission(currentUser.getPerms()); return info; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行了=>认证doGetAuthorizationInfo"); UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; //连接数据库 User user = userService.queryUserByName(userToken.getUsername()); if (user==null){ return null; //UnknownAccountException } //密码认证,shiro自动部署 //密码加密 return new SimpleAuthenticationInfo(user,user.getPwd(),""); } }
- controller/MyController.java
package com.wei.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class MyController { @RequestMapping({"/", "index"}) public String toIndex(Model model) { model.addAttribute("msg", "Hello,Shiro"); return "index"; } @RequestMapping("/user/add") public String add() { return "user/add.html"; } @RequestMapping("/user/update") public String update() { return "user/update.html"; } @RequestMapping("/toLogin") public String toLogin() { return "login"; } @RequestMapping("/login") public String login(String username, String password, Model model) { //获取当前的用户 Subject subject = SecurityUtils.getSubject(); //封装用户的登录数据 UsernamePasswordToken token = new UsernamePasswordToken(username, password); //执行登录方法,如果无异常则成功 try { subject.login(token); return "index"; } catch (UnknownAccountException e) { //用户名不存在 model.addAttribute("msg", "用户名错误"); return "login"; } catch (IncorrectCredentialsException e) { //密码不存在 model.addAttribute("msg", "密码错误"); return "login"; } } //未授权页面 @RequestMapping("/noauth") @ResponseBody public String unauthorized(){ return "未经授权无法访问此页面!"; } }
shiro继承Thymeleaf
- 依赖导入
<!--shiro-thymeleaf--> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.1.0</version> </dependency>
- config/ShiroConifg.java
//整合ShiroDialect:整合shiro和thymeleaf @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); }
- config/UserRealm.java
//获取当前的用户 Subject currentSubject = SecurityUtils.getSubject(); Session session = currentSubject.getSession(); session.setAttribute("loginUser",user);
- index.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro" > <head> <meta charset="UTF-8"> <title>wei_shuo</title> </head> <body> <div> <span><h1>首页</h1></span> <div th:if="${session.loginUser==null}"> <p><a th:href="@{/toLogin}">登录</a> </p> </div> <p th:text="${msg}"></p> <hr> <div shiro:hasPermission="'user:add'"> <a th:href="@{/user/add}"><h1>add</h1></a> </div> <div shiro:hasPermission="'user:update'"> <a th:href="@{/user/update}"><h1>update</h1></a> </div> </div> </body> </html>
🌼 结语:创作不易,如果觉得博主的文章赏心悦目,还请——
点赞
👍收藏
⭐️评论
📝