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

在这里插入图片描述

💗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.mzph.cn/news/15330.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu22.04 DNSSEC(加密DNS服务) configuration

/etx/systemd/resolved.conf是ubuntu下DNS解析服务配置文件&#xff0c;systemd为ubuntu下system and service配置目录 step 1——修改resolved.conf参数 管理员权限打开 /systemd/resolved.conf sudo nano /etc/systemd/resolved.conf修改如下&#xff1a; # This file i…

vr禁毒毒驾模拟体验从源头拒绝毒品,预防毒品

俗话说&#xff0c;一念天堂&#xff0c;一念地狱。吸毒一口&#xff0c;掉入虎口。吸毒对人体的危害非常大&#xff0c;普通人吸毒会导致家破人亡&#xff0c;明星吸毒会毁掉自己的大好星途。没有感同身受&#xff0c;何来悲喜相通&#xff0c;毒品危害认知VR模拟情景体验是VR…

利用频谱仪进行简单的2.4G 频率测试

一、概述 1. 信号源 我们开发2.4G 无线产品的时候&#xff0c;经常需要对产品的无线信号进行测试&#xff0c;以确定精确的频率。在进行频率测试之前&#xff0c;我们的2.4G 射频芯片需要进入单载波模式。 2. 频谱仪 这里选择的是普源的频谱仪。测试范围是 9kHz - 3.2GHz。…

hive 全量表、增量表、快照表、切片表和拉链表

全量表&#xff1a;记录每天的所有的最新状态的数据&#xff0c;增量表&#xff1a;记录每天的新增数据&#xff0c;增量数据是上次导出之后的新数据。快照表&#xff1a;按日分区&#xff0c;记录截止数据日期的全量数据切片表&#xff1a;切片表根据基础表&#xff0c;往往只…

Java-day03(程序流程控制)

程序流程控制 1.顺序结构 程序从上至下逐行执行&#xff0c;无判断与跳转 public class Test1{ public static void main(String[] args){int i 1;int j i 1; System.out.println(j);} }2.分支结构 依据条件&#xff0c;选择性执行某段语句 主要有以下两种 2.1 i…

vue 封装一个鼠标拖动选择时间段功能

<template><div class"timeRange"><div class"calendar"><table><thead><tr><th rowspan"6" class"weekRow"><b>周/时间</b></th><th colspan"24"><…

【机器学习】西瓜书习题3.3Python编程实现对数几率回归

参考代码 结合自己的理解&#xff0c;添加注释。 代码 导入相关的库 import numpy as np import pandas as pd import matplotlib from matplotlib import pyplot as plt from sklearn import linear_model导入数据&#xff0c;进行数据处理和特征工程 # 1.数据处理&#x…

Linux 学习记录60(ARM篇)

Linux 学习记录60(ARM篇) 本文目录 Linux 学习记录60(ARM篇)一、SPI总线1. 概念2. 硬件连接 二、SPI总线协议三、SPI总线通信模式四、对比IIC总线和SPI总线1. 相同点2. 不同点 思维导图 一、SPI总线 1. 概念 1、SPI总结是Motorola首先提出的全双工三线/四线同步串行总线 2、采…

WEB浏览器轻松读写NDEF智能海报、地图坐标、文本标签信息

本示例使用的发卡器&#xff1a;Android Linux RFID读写器NFC发卡器WEB可编程NDEF文本/智能海报/-淘宝网 (taobao.com) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&…

使用pikachu管理工具下的XSS后台进行实战

写在前面的重要提示&#xff1a; Attention&#xff1a;技术没有好坏之分&#xff0c;关键在于使用技术的人或组织。网络安全技术是一把双刃剑 – 作为网络安全人&#xff0c;虽然无法控制头上的帽子是否会变绿&#xff0c;但能控制不让它变黑&#xff1b;无论我们在物质上面对…

redis主从复制哨兵Cluster

目录 前言 一、模式介绍 1.1 主从复制 1.2 哨兵 1.3 集群 二、主从复制 2.1 主从复制的作用 2.2 主从复制流程 2.3 搭建Redis 主从复制 三、Redis 哨兵模式 3.1 哨兵模式原理 3.2 哨兵模式的作用 3.3 哨兵组成结构 3.4 哨兵故障转移机制 3.5 搭建Redis 哨兵模式…

科技项目验收测试:验证软件产品功能与性能的有效手段

科技项目验收测试是验证软件产品功能与性能的重要手段&#xff0c;在项目开发中起到了至关重要的作用。本文将从产品质量、需求验证、性能测试等方面&#xff0c;探讨科技项目验收测试的有效手段。 1、产品质量保证是验收测试的核心 科技项目验收测试的核心目标是验证软件产品…

xshell连接Windows中通过wsl安装的linux子系统-Ubuntu 22.04

xshell连接Windows中通过wsl安装的linux子系统-Ubuntu 22.04 一、安装linux子系统 1.1、 启动或关闭Windows功能-适用于Linux的Windows子系统 1.2 WSL 官方文档 使用 WSL 在 Windows 上安装 Linux //1-安装 WSL 命令 wsl --install//2-检查正在运行的 WSL 版本&#xff1a;…

关于在VS2017中编译Qt项目遇到的问题

关于在VS2017中编译Qt项目遇到的问题 【QT】VS打开QT项目运行不成功 error MSB6006 “cmd.exe”已退出,代码为 2。如何在VS2017里部署的Qt Designer上编辑槽函数 【QT】VS打开QT项目运行不成功 error MSB6006 “cmd.exe”已退出,代码为 2。 链接 如何在VS2017里部署的Qt Design…

10.python设计模式【代理模式】

内容&#xff1a;为其他对象提供一种代理一控制对这个对象的访问 应用场景&#xff1a; 远程代理&#xff1a; 为远程的对象提供代理虚代理&#xff1a;根据需要创建很大的对象保护代理&#xff1a;控制对原始对象的访问&#xff0c;用于对象有不同访问权限时 UML图 举个例…

WIZnet W6100-EVB-Pico DHCP 配置教程(三)

前言 在上一章节中我们讲了网络信息配置&#xff0c;那些网络信息的配置都是用户手动的去配置的&#xff0c;为了能跟电脑处于同一网段&#xff0c;且电脑能成功ping通板子&#xff0c;我们不仅要注意子网掩码&#xff0c;对于IP地址主机位和网络位的划分&#xff0c;而且还要注…

【unity】Pico VR 开发笔记(基础篇)

Pico VR 开发笔记(基础篇) XR Interaction Tooikit 版本 2.3.2 一、环境搭建 其实官方文档已经写的很详细了&#xff0c;这里只是不废话快速搭建&#xff0c;另外有一项官方说明有误的&#xff0c;补充说明一下&#xff0c;在开发工具部分说明 插件安装——安装pico的sdk和XR…

编程小白的自学笔记十二(python爬虫入门四Selenium的使用实例二)

系列文章目录 编程小白的自学笔记十一&#xff08;python爬虫入门三Selenium的使用实例详解&#xff09; 编程小白的自学笔记十&#xff08;python爬虫入门二实例代码详解&#xff09; 编程小白的自学笔记九&#xff08;python爬虫入门代码详解&#xff09; 目录 系列文章…

指针应用基础练习

&#xff08;1&#xff09;一级指针&#xff0c;二级指针 void getString(char **p) {*p "hello world"; }int main(void) {char *str NULL;getString(&str);printf("%s\n", str); } 代码分析&#xff1a; 定义了一个char型指针str&#xff0c;…

uni-app踩坑记

打包h5如何配置域名&#xff1a; 在manifest.json中配置域名 配置完成后无论是测试环境还是正式环境都带上/mobile/&#xff0c;否则会报错404 如何引入调试工具erada: 在默认的index.html中直接引入erada&#xff0c;页面样式会整个错乱&#xff0c;解决方案就是引入官方…