SpringBoot整合Shiro的方法详解

作者:指尖听戏 时间:2022-04-13 15:05:56 

1.Shito简介

1.1 什么是shiro

Apache Shiro是一个java安全(权限)框架

Shiro可以非常容易的开发出足够好的应用,其不仅可以用在javase环境,也可以用在javaee环境

shiro可以完成,认证,授权,加密,会话管理,web集成,缓存等。

下载地址:http://shiro.apache.org/

SpringBoot整合Shiro的方法详解

1.2 有哪些功能

SpringBoot整合Shiro的方法详解

2.QuickStart

Git下载地址

官网下载的可能慢一些

首先新建一个普通的maven项目,然后在项目中导入新的maven项目模块,结构如下:

SpringBoot整合Shiro的方法详解

然后开始创建我们需要的文件,这些文件都可以在官网下载的文件中可以找到:

shiro.ini:

# Users and their assigned roles
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
# -----------------------------------------------------------------------------
[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

导入相关依赖 pom.xml,官网未给出详细的依赖,具体的参考给出的git下载的文件,然后做了一些简单的修改。

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <parent>
       <artifactId>Shiro</artifactId>
       <groupId>com.nuist</groupId>
       <version>1.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>

<artifactId>hello-shiro</artifactId>

<properties>
       <maven.compiler.source>8</maven.compiler.source>
       <maven.compiler.target>8</maven.compiler.target>
   </properties>

<dependencies>
       <dependency>
           <groupId>org.apache.shiro</groupId>
           <artifactId>shiro-core</artifactId>
           <version>1.4.1</version>
       </dependency>

<!-- configure logging -->
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>jcl-over-slf4j</artifactId>
           <version>1.7.21</version>
       </dependency>

<dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-log4j12</artifactId>
           <version>1.7.12</version>
       </dependency>
       <dependency>
           <groupId>log4j</groupId>
           <artifactId>log4j</artifactId>
           <version>1.2.17</version>
       </dependency>

</dependencies>

</project>

log4j.prop[erties:

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

最重要的配置文件:

QuickStart:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.naming.InitialContext;

public class Quickstart {

private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);

public static void main(String[] args) {
       // 旧方法,由于shiro更新无法正常使用
       // Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
       // SecurityManager securityManager = factory.getInstance();

// 新方法 shiro更新问题 解决正常运行
       DefaultSecurityManager securityManager = new DefaultSecurityManager();
       IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
       securityManager.setRealm(iniRealm);

SecurityUtils.setSecurityManager(securityManager);

// Now that a simple Shiro environment is set up, let's see what you can do:

// 获取当前用户对象
       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("subject =>! [" + value + "]");
       }

// 判断当前用户是否被认证
       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 (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.");
       }

// 测试一个简单的权限
       // 粗粒度的一个权限限制
       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!");
       }

//注销功能
       currentUser.logout();

System.exit(0);
   }
}

以上主要的几个关键部分代码:

对象初始化部分

// 新方法 shiro更新问题 解决正常运行
DefaultSecurityManager securityManager = new DefaultSecurityManager();
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
securityManager.setRealm(iniRealm);

SecurityUtils.setSecurityManager(securityManager);

获取当前用户

Subject currentUser = SecurityUtils.getSubject();

判断用户是否被认证

!currentUser.isAuthenticated()

判断用户是否具有什么角色

currentUser.hasRole("schwartz")

判断用户是否拥有权限

currentUser.isPermitted("lightsaber:wield")

注销

currentUser.logout();

此时我们运行启动,项目如下,那么一个简单的shiro quickStart 就已经启动好了。

SpringBoot整合Shiro的方法详解

注意

shir最重要的三个部分:

  • subject 用户

  • SecurityManager 管理所有的用户

  • Realm 连接数据

3.SpringBoot中集成

1.导入shiro相关依赖

<!--
   subject  用户
   SecurityManager 管理所有用户
   Realm 连接数据
   -->
   <!--shiro与springboot-->
   <dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.6.0</version>
   </dependency>

2.自定义UserRealm

package com.nuist.shirospringboot.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;

/**
* @author liuhuanhuan
* @version 1.0
* @date 2022/5/8 17:59
* @Description
*/
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("执行了-》认证doGetAuthenticationInfo");
       return null;
   }
}

3.定义shiroConfig

package com.nuist.shirospringboot.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* @author liuhuanhuan
* @version 1.0
* @date 2022/5/8 17:58
* @Description
*/
@Configuration
public class ShiroConfig {
   // shiroFilterConfiguere

@Bean
   public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager securityManager) {
       ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
       // 设置用户管理器
       shiroFilterFactoryBean.setSecurityManager(securityManager);
       return shiroFilterFactoryBean;
   }
   // defaultWebSecurity
   // 通过@Qualifier 是USerRealm进行绑定
   @Bean
   public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userReaml") UserRealm userReaml) {
       DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
       // 关联Realm
       securityManager.setRealm(userReaml);
       return securityManager;
   }

// 创建realm对象  需要去进行自定义,这样就可以交给spring去进行托管了
   @Bean
   public UserRealm userReaml(){
       return new UserRealm();
   }
}

4.新建页面进行测试

package com.nuist.shirospringboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
* @author liuhuanhuan
* @version 1.0
* @date 2022/5/8 17:52
* @Description
*/
@Controller
public class Mycontrollrt
{
   @RequestMapping({"/","/index"})
   public String toIndex(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";
   }
}

index.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}"></p>
<hr>
<a th:href="@{/user/add}">add </a>  |  <a th:href="@{/user/update}">update</a>
</body>
</html>

user/add.html

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
<h1>add</h1>
</body>
</html>

user/update.html

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>

<h1>update</h1>

</body>
</html>

此时一个简单的项目就搭建起来了,然后开始shiro的认证授权的操作。

我们只需要在配置中添加如下代码:

Map<String,String> filterMap = new LinkedHashMap<>();
//        filterMap.put("/user/add","authc");
//        filterMap.put("/user/update","authc");
       filterMap.put("/user/*","authc");
       shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

shiroFilterFactoryBean.setLoginUrl("/toLogin");

此时就对我们所有的页面请求进行了拦截,然后转发到login的页面

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>

<h1>login</h1>
<hr>
<form action="">
   <p>用户名:<input type="text" name="username"></p>
   <p>密码:<input type="password" name="password"></p>
   <p><input type="submit"></p>
</form>
</body>
</html>

此时在页面进行点击的时候,我们就无法正常进入页面,只能进入到我们的登录页面

进行登录验证的拦截,只有输入正确的账号密码才能够进入:

MyCOntroller中新增如下代码:

@RequestMapping("/login")
   public String login(String username,String password,Model model) {
       // 获取当前用户
       Subject subject = SecurityUtils.getSubject();
       // 封装当前用户
       UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
       try {
           subject.login(usernamePasswordToken); // 执行登录的方法,有异常进行处理
           return "index";
       } catch (UnknownAccountException e){
           model.addAttribute("msg","用户名错误");
           return "login";
       } catch (IncorrectCredentialsException e) { // 密码不存在
           model.addAttribute("msg","密码错误");
           return "login";
       }
   }

然后修改login页面的代码

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>

<h1>login</h1>
<hr>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}">
   <p>用户名:<input type="text" name="username"></p>
   <p>密码:<input type="password" name="password"></p>
   <p><input type="submit"></p>
</form>
</body>
</html>

然后在我们的UserRealm

中doGetAuthenticationInfo方法中新增代码

@Override
   protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
       System.out.println("执行了-》认证doGetAuthenticationInfo");
       String name = "root";
       String password = "123456";
       UsernamePasswordToken authenticationToken1 = (UsernamePasswordToken) authenticationToken;
       if (!authenticationToken1.getUsername().equals(name)) {
           return null;   // 抛出异常
       }
       // 密码认证  shiro去做
       return new SimpleAuthenticationInfo("",password,"");
   }

此时我们通过UsernamePasswordToken 获取我们封装好的账号和密码,但是我们只需要进行账号的认证,密码的认证交给我们的shiro去做就可以了。

具体的用户授权,我们可以进行连接数据库的设置,但是我为了偷懒,我就不去手动创建与数据库的链接啦。

下面我们来进行页面的授权操作

我们想要进行用户的授权操作

我们需要在shiroConfig中新增如下代码:

filterMap.put("/user/add","perms[user:add]");

此时代表如果用户拥有user:add操作的话,可以显示,如果没有的话就不能正常显示

我们可以自定义一个页面来用于返回信息的显示:

@RequestMapping("/noauth")    
@ResponseBody                
public String noauth() {      
    return "未经过授权无法进行访问";    
}                            

当用户没有add权限的时候,我们就提示无法显示:

此时我们就完成了单个的用户授权的操作。此时我们再去进行具体的页面操作

shiro与thymeleaf的结合:

需要将我们进行验证的页面进行如下操作:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
     xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>

<h1>首页</h1>
<p>
   <a th:href="@{/toLogin}">登录</a>
</p>

<p th:text="${msg}"></p>
<hr>
<div shiro:hasPermission="user:add">
   <a th:href="@{/user/add}">add </a>
</div>
|
<div shiro:hasPermission="user:update">
   <a th:href="@{/user/update}">update</a>
</div>
</body>
</html>

此时应用的方式和springsecurity的方式基本一致。

然后在我们的授权页面进行操作如下:

UserRealm中修改doGetAuthorizationInfo方法,如下:

@Override
   protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
       System.out.println("执行了=》授权doGetAuthorizationInfo方法");
       SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

// 新增授权页面
       authorizationInfo.addStringPermission("user:add");

// 拿到当前对象 ,然后通过对象中的授权方式进行判断

return authorizationInfo;
   }

此时我们赋予用户只有add的权限,那么按理说在页面中是无法显示update的按钮,那么我们进行测试下,是否可以正常使用:

SpringBoot整合Shiro的方法详解

此时认证授权部分已经成功啦,以上就是我们进行的一个小小的demo,更深入的学习,后续继续更新。

git源码地址

来源:https://blog.csdn.net/qq_38140292/article/details/124654909

标签:SpringBoot,整合,Shiro
0
投稿

猜你喜欢

  • java map转Multipart/form-data类型body实例

    2023-04-19 13:16:18
  • Java 超详细讲解类的定义方式和对象的实例化

    2021-09-11 17:38:29
  • SpringBoot工程搭建打包、启动jar包和war包的教程图文详解

    2022-05-04 22:09:10
  • Spring Boot教程之必须了解的核心概念

    2022-07-15 14:17:24
  • SpringBoot配置拦 截器实现过程详解

    2023-11-24 17:14:58
  • C# Winform消息通知之系统本地通知local toast notification

    2023-02-01 04:14:02
  • java 中死锁问题的实例详解

    2022-08-18 02:22:39
  • Java实现两人五子棋游戏(二) 画出棋盘

    2022-08-26 15:09:57
  • Java 定时器(Timer,TimerTask)详解及实例代码

    2022-08-17 20:03:11
  • springBoot系列常用注解(小结)

    2023-12-17 23:26:45
  • java语言实现猜数字游戏

    2023-11-24 00:19:38
  • Java11中基于嵌套关系的访问控制优化详解

    2021-12-28 18:54:20
  • 解决java 分割字符串成数组时,小圆点不能直接进行分割的问题

    2023-11-05 03:13:24
  • Spring JPA联表查询之OneToOne源码详解

    2022-08-06 13:57:55
  • 详解在Spring Boot中使用JPA

    2022-08-03 15:44:13
  • Flutter加载图片流程MultiFrameImageStreamCompleter解析

    2023-07-19 02:45:55
  • 基于Mock测试Spring MVC接口过程解析

    2023-11-27 12:04:30
  • SpringBoot启动访问localhost:8080报错404的解决操作

    2021-07-20 08:44:00
  • 教你用JAVA写文本编辑器(四)

    2023-10-18 18:30:16
  • C#用户定义类型转换详解

    2022-06-07 11:44:32
  • asp之家 软件编程 m.aspxhome.com