SpringBoot原理分析 | 安全框架:Shiro

news/2024/5/19 5:29:42

在这里插入图片描述

💗wei_shuo的个人主页

💫wei_shuo的学习社区

🌐Hello World !


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, stdoutlog4j.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();//通过当前用户,取出sessionSession 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();//关联UserRealmsecurityManager.setRealm(userRealm);return securityManager;}//Realm对象@Beanpublic 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 {//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了=>授权doGetAuthorizationInfo");return null;}//认证@Overrideprotected 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=UTCdriver-class-name: com.mysql.cj.jdbc.Driver#切换数据源type: com.alibaba.druid.pool.DruidDataSource#Spring Boot 默认是不注入这些属性值的,需要自己绑定#druid 数据源专有配置initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入#如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4jfilters: stat,wall,log4jmaxPoolPreparedStatementPerConnectionSize: 20useGlobalDataSourceStat: trueconnectionProperties: 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 mapperPUBLIC "-//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 {@AutowiredUserServiceImpl userService;@Testvoid 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 {@AutowiredUserServiceImpl userService;@Testvoid 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 {@AutowiredUserService userService;//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了=>授权doGetAuthorizationInfo");return null;}//认证@Overrideprotected 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();//关联UserRealmsecurityManager.setRealm(userRealm);return securityManager;}//Realm对象@Beanpublic 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 {@AutowiredUserService userService;//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了=>授权doGetAuthorizationInfo");//SimpleAuthorizationInfoSimpleAuthorizationInfo 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;}//认证@Overrideprotected 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")@ResponseBodypublic 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@Beanpublic 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>

🌼 结语:创作不易,如果觉得博主的文章赏心悦目,还请——点赞👍收藏⭐️评论📝


在这里插入图片描述


http://www.mrgr.cn/p/56302451

相关文章

Redis以及Java使用Redis

一、Redis的安装 Redis是一个基于内存的 key-value 结构数据库。 基于内存存储&#xff0c;读写性能高 适合存储热点数据&#xff08;热点商品、资讯、新闻&#xff09; 企业应用广泛 官网&#xff1a;https://redis.io 中文网&#xff1a;https://www.redis.net.cn/ Redis…

.NET网络编程——TCP通信

一、网络编程的基本概念 : 1. 网络 就是将不同区域的电脑连接到一起&#xff0c;组成局域网、城域网或广域网。把分部在不同地理区域的计算机于专门的外部设备用通信线路 互联成一个规模大、功能强的网络系统&#xff0c;从而使众多的计算机可以方便地互相传递信息&#xff0c…

Zabbix分布式监控Web监控

目录 1 概述2 配置 Web 场景2.1 配置步骤2.2 显示 3 Web 场景步骤3.1 创建新的 Web 场景。3.2 定义场景的步骤3.3 保存配置完成的Web 监控场景。 4 Zabbix-Get的使用 1 概述 您可以使用 Zabbix 对多个网站进行可用性方面监控&#xff1a; 要使用 Web 监控&#xff0c;您需要定…

QtC++ 技术分析4 - 流、d-pointer隐式共享以及容器迭代器

目录 QT 中的流文件系统与底层文件操作文件系统类 QFile QTextStreamQDataStreamQLocale 隐式共享与 d-pointer隐式共享d-pointer 在隐式共享中的应用二进制代码兼容d-pointer 模式实现 Qt 容器及迭代器QTL 概述几种常见的迭代器及其对应类型QTL 容器对应迭代器通用算法函子&am…

超全整理,Jmeter性能测试-常用Jmeter第三方插件详解(超细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Jmeter作为一个开…

批量插入数据、MVC三层分离

八、批量插入数据 1、使用Statement&#xff08;&#xff09; 2、使用PreparedStatement() 3、使用批量操作API 4、优化 九、MVC三层分离

Windows下安装HBase

Windows下安装HBase 一、HBase简介二、HBase下载安装包三、环境准备3.1、 JDK的安装3.2、 Hadoop的安装 四、HBase安装4.1、压缩包解压为文件夹4.2、配置环境变量4.3、%HBASE_HOME%目录下新建临时文件夹4.4、修改配置文件 hbase-env.cmd4.4.1、配置JAVA环境4.4.2、set HBASE_MA…

高等数学中如何求间断点

高等数学中求间断点是一项重要的技巧&#xff0c;特别适用于分析函数的性质和图像的特征。在本文中&#xff0c;我们将深入探讨如何在给定函数中找到间断点&#xff0c;并解释其数学原理和实际应用。 什么是间断点&#xff1f; 在高等数学中&#xff0c;间断点是指函数在某个点…

加利福尼亚大学|3D-LLM:将3D世界于大规模语言模型结合

来自加利福尼亚大学的3D-LLM项目团队提到&#xff1a;大型语言模型 (LLM) 和视觉语言模型 (VLM) 已被证明在多项任务上表现出色&#xff0c;例如常识推理。尽管这些模型非常强大&#xff0c;但它们并不以 3D 物理世界为基础&#xff0c;而 3D 物理世界涉及更丰富的概念&#xf…

windows下载安装FFmpeg

FFmpeg是一款强大的音视频处理软件&#xff0c;下面介绍如何在windows下下载安装FFmpeg 下载 进入官网: https://ffmpeg.org/download.html, 选择Windows, 然后选择"Windows builds from gyan.dev" 在弹出的界面中找到release builds, 然后选择一个版本&#xff0…

亚马逊云科技全新Amazon Bedrock,助力客户构建生成式AI应用

亚马逊云科技近日在纽约峰会上宣布全面扩展其全托管基础模型服务Amazon Bedrock&#xff0c;包括新增Cohere作为基础模型供应商&#xff0c;加入Anthropic和Stability AI的最新基础模型&#xff0c;并发布变革性的新功能Amazon Bedrock Agents功能。客户无需管理任何基础设施&a…

Jenkins 安装构建

一、CentOS 安装 1. 使用该存储库 sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key 2. 安装 Java yum install fontconfig java-11-openjdk配…

java实现文件下载

1.文件上传 文件上传&#xff0c;也称为upload&#xff0c;是指将本地图片、视频、音频等文件上传到服务器上&#xff0c;可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛&#xff0c;我们经常发微博、发微信朋友圈都用到了文件上传功能。 import com.itheima.…

前端Web实战:从零打造一个类Visio的流程图拓扑图绘图工具

前言 大家好&#xff0c;本系列从Web前端实战的角度&#xff0c;给大家分享介绍如何从零打造一个自己专属的绘图工具&#xff0c;实现流程图、拓扑图、脑图等类Visio的绘图工具。 你将收获 免费好用、专属自己的绘图工具前端项目实战学习如何从0搭建一个前端项目等基础框架项…

spring6——容器

文章目录 容器&#xff1a;IocIoc容器控制反转&#xff08;Ioc&#xff09;依赖注入IoC容器在Spring的实现 基于XML管理Bean搭建环境获取bean依赖注入setter注入构造器注入特殊值处理字面量赋值null值xml实体CDATA节 特殊类型属性注入为对象类型属性赋值方式一&#xff1a;引入…

音频开发-小程序和H5

微信录音 1、引入sdk 2、录音操作 浏览器录音 参考文献&#xff1a;前端H5实现调用麦克风&#xff0c;录音功能_h5 录音_Darker丨峰神的博客-CSDN博客 function record() { window.navigator.mediaDevices.getUserMedia({ audio: { sampleRate: 44100, // 采样率 channelCount…

【软件安装】MATLAB_R2021b for mac 安装

Mac matlab_r2021b 安装 下载链接&#xff1a;百度网盘 下载链接中所有文件备用。 我所使用的电脑配置&#xff1a; Macbook Pro M1 Pro 16512 系统 macOS 13.5 安装步骤 前置准备 无此选项者&#xff0c;自行百度 “mac 任何来源”。 1 下载好「MATLAB R2021b」安装文…

Leetcode-每日一题【剑指 Offer 56 - I. 数组中数字出现的次数】

题目 一个整型数组 nums 里除两个数字之外&#xff0c;其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n)&#xff0c;空间复杂度是O(1)。 示例 1&#xff1a; 输入&#xff1a;nums [4,1,4,6]输出&#xff1a;[1,6] 或 [6,1] 示例 2&#x…

计算机网络——传输层

文章目录 **1 传输层提供的服务****1.1 传输层的功能****1.2 传输层的寻址与端口** **2 UDP协议****2.1 UDP数据报****2.2 UDP校验** **3 TCP协议****3.1 TCP协议的特点****3.2 TCP报文段****3.3 TCP连接管理****3.4 TCP可靠传输****3.5 TCP流量控制****3.6 TCP拥塞控制** 1 传…

Verilog语法学习——LV4_移位运算与乘法

LV4_移位运算与乘法 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 题目描述&#xff1a; 已知d为一个8位数&#xff0c;请在每个时钟周期分别输出该数乘1/…