Shiro简介
- Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理
- 三个核心组件:Subject, SecurityManager 和 Realms
- Subject代表了当前用户的安全操作
- SecurityManager管理所有用户的安全操作,是Shiro框架的核心,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
- Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
- Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro快速入门
导入依赖
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-core</artifactId>
- <version>1.7.1</version>
- </dependency>
- <!-- configure logging -->
- <!-- https://mvnrepository.com/artifact/org.slf4j/jcl-over-slf4j -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>jcl-over-slf4j</artifactId>
- <version>2.0.0-alpha1</version>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>2.0.0-alpha1</version>
- </dependency>
- <dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.17</version>
- </dependency>
配置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
配置Shiro.ini(在IDEA中需要导入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 with assigned permissions
- #
- # Each line conforms to the format defined in the
- # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
- # -----------------------------------------------------------------------------
- [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.DefaultSecurityManager;
- import org.apache.shiro.realm.text.IniRealm;
- 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);
- /*
- Shiro三大对象:
- Subject: 用户
- SecurityManager:管理所有用户
- Realm: 连接数据
- */
- public static void main(String[] args) {
- // 创建带有配置的Shiro SecurityManager的最简单方法
- // realms, users, roles and permissions 是使用简单的INI配置。
- // 我们将使用可以提取.ini文件的工厂来完成此操作,
- // 返回一个SecurityManager实例:
- // 在类路径的根目录下使用shiro.ini文件
- // (file:和url:前缀分别从文件和url加载):
- //Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
- //SecurityManager securityManager = factory.getInstance();
- DefaultSecurityManager securityManager = new DefaultSecurityManager();
- IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
- securityManager.setRealm(iniRealm);
- // 对于这个简单的示例快速入门,请使SecurityManager
- // 可作为JVM单例访问。大多数应用程序都不会这样做
- // 而是依靠其容器配置或web.xml进行
- // webapps。这超出了此简单快速入门的范围,因此
- // 我们只做最低限度的工作,这样您就可以继续感受事物.
- SecurityUtils.setSecurityManager(securityManager);
- // 现在已经建立了一个简单的Shiro环境,让我们看看您可以做什么:
- // 获取当前用户对象 Subject
- Subject currentUser = SecurityUtils.getSubject();
- // 使用Session做一些事情(不需要Web或EJB容器!!!
- Session session = currentUser.getSession();//通过当前用户拿到Session
- session.setAttribute("someKey", "aValue");
- String value = (String) session.getAttribute("someKey");
- if (value.equals("aValue")) {
- log.info("Retrieved the correct value! [" + value + "]");
- }
- // 判断当前用户是否被认证
- if (!currentUser.isAuthenticated()) {
- //token : 令牌,没有获取,随机
- 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 (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);//退出
- }
- }
启动测试
SpringBoot-Shiro整合(最后会附上完整代码)
前期工作
导入shiro-spring整合包依赖
- <!-- shiro-spring整合包 -->
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-spring</artifactId>
- <version>1.7.1</version>
- </dependency>
跳转的页面
index.html
- <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
- <head>
- <meta charset="UTF-8">
- <title>首页</title>
- </head>
- <body>
- <h1>首页</h1>
- <p th:text="${msg}"></p>
- <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>
- </body>
- </html>
add.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>add</title>
- </head>
- <body>
- <p>add</p>
- </body>
- </html>
update.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>update</title>
- </head>
- <body>
- <p>update</p>
- </body>
- </html>
编写shiro的配置类ShiroConfig.java
- package com.example.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 {
- //3. ShiroFilterFactoryBean
- @Bean
- public ShiroFilterFactoryBean getshiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
- ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
- //设置安全管理器
- factoryBean.setSecurityManager(defaultWebSecurityManager);
- return factoryBean;
- }
- //2.创建DefaultWebSecurityManager
- @Bean(name = "SecurityManager")
- public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
- DefaultWebSecurityManager SecurityManager=new DefaultWebSecurityManager();
- //3.关联Realm
- SecurityManager.setRealm(userRealm);
- return SecurityManager;
- }
- //1.创建Realm对象
- @Bean(name = "userRealm")
- public UserRealm userRealm(){
- return new UserRealm();
- }
- }
编写UserRealm.java
- package com.example.config;
- import org.apache.shiro.authc.AuthenticationException;
- import org.apache.shiro.authc.AuthenticationInfo;
- import org.apache.shiro.authc.AuthenticationToken;
- import org.apache.shiro.authz.AuthorizationInfo;
- import org.apache.shiro.realm.AuthorizingRealm;
- import org.apache.shiro.subject.PrincipalCollection;
- public class UserRealm extends AuthorizingRealm {
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
- System.out.println("授权");
- return null;
- }
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
- System.out.println("认证");
- return null;
- }
- }
编写controller测试环境是否搭建好
- package com.example.controller;
- 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 index(Model model){
- model.addAttribute("msg","hello,shiro");
- return "index";
- }
- @RequestMapping("/user/add")
- public String add(){
- return "user/add";
- }
- @RequestMapping("/user/update")
- public String update(){
- return "user/update";
- }
- }
实现登录拦截
在ShiroConfig.java文件中添加拦截
- Map<String,String> filterMap = new LinkedHashMap<>();
- //对/user/*下的文件只有拥有authc权限的才能访问
- filterMap.put("/user/*","authc");
- //将Map存放到ShiroFilterFactoryBean中
- factoryBean.setFilterChainDefinitionMap(filterMap);
这样,代码跑起来,你点击add或者update就会出现404错误,这时候,我们再继续添加,让它跳转到我们自定义的登录页
添加登录拦截到登录页
- //需进行权限认证时跳转到toLogin
- factoryBean.setLoginUrl("/toLogin");
- //权限认证失败时跳转到unauthorized
- factoryBean.setUnauthorizedUrl("/unauthorized");
login.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>登录</title>
- </head>
- <body>
- <form action="">
- 用户名:<input type="text" name="username"><br>
- 密码:<input type="text" name="password"><br>
- <input type="submit">
- </form>
- </body>
- </html>
视图跳转添加一个login页面跳转
- @RequestMapping("/toLogin")
- public String login(){
- return "login";
- }
上面,我们已经成功拦截了,现在我们来实现用户认证
首先,我们需要一个登录页面
login.html
- <!DOCTYPE html>
- <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
- <head>
- <meta charset="UTF-8">
- <title>登录</title>
- </head>
- <body>
- <p th:text="${msg}" style="color: red"></p>
- <form th:action="@{/login}">
- 用户名:<input type="text" name="username"><br>
- 密码:<input type="text" name="password"><br>
- <input type="submit">
- </form>
- </body>
- </html>
其次,去controller编写跳转到登录页面
- @RequestMapping("/login")
- public String login(String username,String password,Model model){
- //获得当前的用户
- Subject subject = SecurityUtils.getSubject();
- //封装用户数据
- UsernamePasswordToken taken = new UsernamePasswordToken(username,password);
- try{//执行登陆操作,没有发生异常就说明登陆成功
- subject.login(taken);
- return "index";
- }catch (UnknownAccountException e){
- model.addAttribute("msg","用户名错误");
- return "login";
- }catch (IncorrectCredentialsException e){
- model.addAttribute("msg","密码错误");
- return "login";
- }
- }
最后去UserRealm.java配置认证
- //认证
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
- System.out.println("认证");
- String name = "root";
- String password = "123456";
- UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
- if (!userToken.getUsername().equals(name)){
- return null;//抛出异常 用户名错误那个异常
- }
- //密码认证,shiro自己做
- return new SimpleAuthenticationInfo("",password,"");
- }
运行测试,成功!!!
附上最后的完整代码
pom.xml引入的依赖
pom.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.4.4</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
- <groupId>com.example</groupId>
- <artifactId>springboot-08-shiro</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <name>springboot-08-shiro</name>
- <description>Demo project for Spring Boot</description>
- <properties>
- <java.version>1.8</java.version>
- </properties>
- <dependencies>
- <!-- shiro-spring整合包 -->
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-spring</artifactId>
- <version>1.7.1</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-thymeleaf</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
- </project>
静态资源
index.html
- <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
- <head>
- <meta charset="UTF-8">
- <title>首页</title>
- </head>
- <body>
- <h1>首页</h1>
- <p th:text="${msg}"></p>
- <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>
- </body>
- </html>
login.html
- <!DOCTYPE html>
- <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
- <head>
- <meta charset="UTF-8">
- <title>登录</title>
- </head>
- <body>
- <p th:text="${msg}" style="color: red"></p>
- <form th:action="@{/login}">
- 用户名:<input type="text" name="username"><br>
- 密码:<input type="text" name="password"><br>
- <input type="submit">
- </form>
- </body>
- </html>
add.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>add</title>
- </head>
- <body>
- <p>add</p>
- </body>
- </html>
update.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>update</title>
- </head>
- <body>
- <p>update</p>
- </body>
- </html>
controller层
MyController.java
- package com.example.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 index(Model model){
- model.addAttribute("msg","hello,shiro");
- return "index";
- }
- @RequestMapping("/user/add")
- public String add(){
- return "user/add";
- }
- @RequestMapping("/user/update")
- public String update(){
- return "user/update";
- }
- @RequestMapping("/toLogin")
- public String toLogin(){
- return "login";
- }
- @RequestMapping("/login")
- public String login(String username,String password,Model model){
- //获得当前的用户
- Subject subject = SecurityUtils.getSubject();
- //封装用户数据
- UsernamePasswordToken taken = new UsernamePasswordToken(username,password);
- try{//执行登陆操作,没有发生异常就说明登陆成功
- subject.login(taken);
- return "index";
- }catch (UnknownAccountException e){
- model.addAttribute("msg","用户名错误");
- return "login";
- }catch (IncorrectCredentialsException e){
- model.addAttribute("msg","密码错误");
- return "login";
- }
- }
- }
config文件
ShiroConfig.java
- package com.example.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 {
- //4. ShiroFilterFactoryBean
- @Bean
- public ShiroFilterFactoryBean getshiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
- ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
- //5. 设置安全管理器
- factoryBean.setSecurityManager(defaultWebSecurityManager);
- /* shiro内置过滤器
- anon 无需授权、登录就可以访问,所有人可访。
- authc 需要登录授权才能访问。
- authcBasic Basic HTTP身份验证拦截器
- logout 退出拦截器。退出成功后,会 redirect到设置的/URI
- noSessionCreation 不创建会话连接器
- perms 授权拦截器,拥有对某个资源的权限才可访问
- port 端口拦截器
- rest rest风格拦截器
- roles 角色拦截器,拥有某个角色的权限才可访问
- ssl ssl拦截器。通过https协议才能通过
- user 用户拦截器,需要有remember me功能方可使用
- */
- Map<String,String> filterMap = new LinkedHashMap<>();
- //对/user/*下的文件只有拥有authc权限的才能访问
- filterMap.put("/user/*","authc");
- //将Map存放到ShiroFilterFactoryBean中
- factoryBean.setFilterChainDefinitionMap(filterMap);
- //需进行权限认证时跳转到toLogin
- factoryBean.setLoginUrl("/toLogin");
- //权限认证失败时跳转到unauthorized
- factoryBean.setUnauthorizedUrl("/unauthorized");
- return factoryBean;
- }
- //2.创建DefaultWebSecurityManager
- @Bean(name = "SecurityManager")
- public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
- DefaultWebSecurityManager SecurityManager=new DefaultWebSecurityManager();
- //3.关联Realm
- SecurityManager.setRealm(userRealm);
- return SecurityManager;
- }
- //1.创建Realm对象
- @Bean(name = "userRealm")
- public UserRealm userRealm(){
- return new UserRealm();
- }
- }
UserRealm.java
- package com.example.config;
- import org.apache.shiro.authc.*;
- import org.apache.shiro.authz.AuthorizationInfo;
- import org.apache.shiro.realm.AuthorizingRealm;
- import org.apache.shiro.subject.PrincipalCollection;
- public class UserRealm extends AuthorizingRealm {
- //授权
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
- System.out.println("授权");
- return null;
- }
- //认证
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
- System.out.println("认证");
- String name = "root";
- String password = "123456";
- UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
- if (!userToken.getUsername().equals(name)){
- return null;//抛出异常 用户名错误那个异常
- }
- //密码认证,shiro自己做
- return new SimpleAuthenticationInfo("",password,"");
- }
- }
但是,我们在用户认证这里,真实情况是从数据库中取的,所以,我们接下来去实现一下从数据库中取出数据来实现用户认证
Shiro整合mybatis
前期工作
在前面导入的依赖中,继续添加以下依赖
- <!-- 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.5</version>
- </dependency>
- <!-- 引入mybatis -->
- <dependency>
- <groupId>org.mybatis.spring.boot</groupId>
- <artifactId>mybatis-spring-boot-starter</artifactId>
- <version>2.1.4</version>
- </dependency>
- <!-- lombok -->
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- </dependency>
导入了mybatis和Druid,就去application.properties配置一下和Druid
Druid
- spring:
- datasource:
- username: root
- password: 123456
- url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
- 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
mybatis
- mybatis:
- type-aliases-package: com.example.pojo
- mapper-locations: classpath:mapper/*.xml
连接数据库
编写实体类
- package com.example.pojo;
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class User {
- private Integer id;
- private String name;
- private String pwd;
- }
编写mapper
- package com.example.mapper;
- import com.example.pojo.User;
- import org.apache.ibatis.annotations.Mapper;
- import org.springframework.stereotype.Repository;
- @Repository
- @Mapper
- public interface UserMapper {
- public User getUserByName(String name);
- }
编写mapper.xml
- <?xml version="1.0" encoding="UTF8" ?>
- <!DOCTYPE mapper
- PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.example.mapper.UserMapper">
- <select id="getUserByName" parameterType="String" resultType="User">
- select * from mybatis.user where name=#{name}
- </select>
- </mapper>
编写service
- package com.example.service;
- import com.example.pojo.User;
- public interface UserService {
- public User getUserByName(String name);
- }
- package com.example.service;
- import com.example.mapper.UserMapper;
- import com.example.pojo.User;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- @Service
- public class UserServiceImpl implements UserService{
- @Autowired
- UserMapper userMapper;
- @Override
- public User getUserByName(String name) {
- return userMapper.getUserByName(name);
- }
- }
使用数据库中的数据
修改UserRealm.java即可
- package com.example.config;
- import com.example.pojo.User;
- import com.example.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;
- public class UserRealm extends AuthorizingRealm {
- @Autowired
- UserService userService;
- //授权
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
- System.out.println("授权");
- return null;
- }
- //认证
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
- System.out.println("认证");
- UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
- //连接真实的数据库
- User user = userService.getUserByName(userToken.getUsername());
- if (user==null){
- return null;//抛出异常 用户名错误那个异常
- }
- //密码认证,shiro自己做
- return new SimpleAuthenticationInfo("",user.getPwd(),"");
- }
- }
认证搞完了,我们再来看看授权
在ShiroConfig.java文件加入授权,加入这行代码: filterMap.put("/user/add","perms[user:add]");//只有拥有user:add权限的人才能访问add,注意授权的位置在认证前面,不然授权会认证不了;
运行测试:add页面无法访问
授权同理:filterMap.put("/user/update","perms[user:update]");//只有拥有user:update权限的人才能访问update
自定义一个未授权跳转页面
在ShiroConfig.java文件设置未授权时跳转到unauthorized页面,加入这行代码:
factoryBean.setUnauthorizedUrl("/unauthorized"); 2. 去Mycontroller写跳转未授权页面
- @RequestMapping("/unauthorized")
- @ResponseBody//懒得写界面,返回一个字符串
- public String unauthorized(){
- return "没有授权,无法访问";
- }
运行效果:
从数据库中接受用户的权限,进行判断
在数据库中添加一个属性perms,相应的实体类也要修改
修改UserRealm.java
- package com.example.config;
- import com.example.pojo.User;
- import com.example.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;
- public class UserRealm extends AuthorizingRealm {
- @Autowired
- UserService userService;
- //授权
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
- System.out.println("授权");
- SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- //没有使用数据库,直接自己设置的用户权限,给每个人都设置了,现实中要从数据库中取
- //info.addStringPermission("user:add");
- //从数据库中得到权限信息
- //获得当前登录的对象
- Subject subject = SecurityUtils.getSubject();
- //拿到User对象,通过getPrincipal()获得
- User currentUser = (User) subject.getPrincipal();
- //设置当前用户的权限
- info.addStringPermission(currentUser.getPerms());
- return info;
- }
- //认证
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
- System.out.println("认证");
- UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
- //连接真实的数据库
- User user = userService.getUserByName(userToken.getUsername());
- if (user==null){
- return null;//抛出异常 用户名错误那个异常
- }
- //密码认证,shiro自己做
- return new SimpleAuthenticationInfo(user,user.getPwd(),"");
- }
- }
有了授权后,就又出现了一个问题,我们是不是要让用户没有权限的东西,就看不见呢?这时候,就出现了Shiro-thymeleaf整合
Shiro-thymeleaf整合
导入整合的依赖
- <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
- <dependency>
- <groupId>com.github.theborakompanioni</groupId>
- <artifactId>thymeleaf-extras-shiro</artifactId>
- <version>2.0.0</version>
- </dependency>
在ShiroConfig整合ShiroDialect
- //整合ShiroDialect: 用来整合 shiro thymeleaf
- @Bean
- public ShiroDialect getShiroDialect(){
- return new ShiroDialect();
- }
修改index页面
- <html lang="en" xmlns:th="http://www.thymeleaf.org"
- xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
- <!-- 三个命名空间
- xmlns:th="http://www.thymeleaf.org"
- xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
- xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
- -->
- <head>
- <meta charset="UTF-8">
- <title>首页</title>
- </head>
- <body>
- <h1>首页</h1>
- <p th:text="${msg}"></p>
- <!--判断是否有用户登录,如果有就不显示登录按钮-->
- <div th:if="${session.loginUser==null}">
- <a th:href="@{/toLogin}" rel="external nofollow" >登录</a>
- </div>
- <div shiro:hasPermission="user:add">
- <a th:href="@{/user/add}" rel="external nofollow" rel="external nofollow" rel="external nofollow" >add</a>
- </div>
- <div shiro:hasPermission="user:update">
- <a th:href="@{/user/update}" rel="external nofollow" rel="external nofollow" rel="external nofollow" >update</a>
- </div>
- </body>
- </html>
判断是否有用户登录
- //这个是整合shiro和thymeleaf用到的,让登录按钮消失的判断
- Subject subject = SecurityUtils.getSubject();
- Session session = subject.getSession();
- session.setAttribute("loginUser", user);
测试
以上就是Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)的详细内容,更多关于Java安全框架——Shiro的资料请关注服务器之家其它相关文章!
原文链接:https://juejin.cn/post/6949793422483914789