当前位置:   article > 正文

Shiro安全框架【SpringBoot版】_shiro版本

shiro版本


Shiro安全框架

一、 入门概述

1.1、Shiro是什么

Apache Shiro 是一款功能强大的且易于使用的Java的安全框架。Shiro可以完成:认证、加密、会话管理、与web集集成等。借助SHiro可以帮助我们快速轻松的保护任何应用程序。

shiro官网:Apache Shiro | Simple. Java. Security.

在这里插入图片描述

1.2、为什么使用Shiro

与Shiro的特性密不可分:

  • 易于使用
  • 全面
  • 灵活
  • 强力支持Web
  • 兼容性强
  • 社区支持

1.3、Shiro与Spring Security的区别

  1. SpringSecurity基于Spring开发,项目若使用Spring 可以与SpringSecurity作权限更加方便,而Shiro需要与Spring进行整合

  2. Spring Security功能更加丰富

  3. Spring Security社区资源更加丰富

看到这里,是不是有些人就认为Spring Security功能更发面都比Shiro好,为什么不学习SpringSecurity。有一句话:存在即合理。下面看看Shiro的特点

  1. Shiro的配置和使用比较简单,SpringSecurity使用比较复杂

  2. Shiro的依赖性低,不需要任何的容器与框架,可以独立运行

  3. Shiro不仅仅可以使用在Web端,可以使用在任何的场景。

1.4、基本功能

了解Shiro的功能,我们可以去官网下载一张Shiro的功能结构图来进行补充学习:

阿帕奇四郎特色

1.4.1、主要功能
  1. 认证登录(Authentication)
  2. 授权验证(Authorization)
  3. 会话管理(Session Management)
  4. 密码加密(Cryptography)
1.4.2 、次要功能
  1. Web支持(web support)
  2. 缓存(caching)
  3. 多线程并发验证(Concurrency)
  4. 测试(Testing)
  5. 另外身份登录(Run as)
  6. 记住我(Remember me)

1.5、架构原理

从外部来看Shiro,即从应用程序的角度来观察使用Shiro完成工作

Shiro Basic Architecture Diagram

应用程序—>(登录)---->subject(对象)进行身份校验---->安全管理器(SecurityManager)---->Reaim(用户登陆的用户信息)

从内部的架构来看Shiro

Shiro Architecture Diagram

二、基本使用

2.1、环境准备

1、Shiro不依赖容器,可以直接利用Maven使用

2、添加依赖

<!-- Shiro依赖 -->
  <dependencies>
  	<dependency>
  		<groupId>org.apache.shiro</groupId>
  		<artifactId>shiro-core</artifactId>
  		<version>1.9.0</version>
  	</dependency>
  	
  	<dependency>
  		<groupId>commons-logging</groupId>
  		<artifactId>commons-logging</artifactId>
  		<version>1.2</version>
  	</dependency>
  </dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3、创建Maven工程

结构如下:

在这里插入图片描述

2.2、配置ini文件

在创建好的工程的Resources目录下,创建一个shiro.ini文件

在这里插入图片描述

[users]
zhangsan=z3
lisi=l4
  • 1
  • 2
  • 3

2.3、登录认证

2.3.1、登录认证概念

(1)身份认证:一般需要提供身份ID等一些表示用户登陆这信息身份的标识,如提供email、用户名\密码来认证

(2)在Shiro中、用户需要提供principals(身份)和credentials(证明)给shiro。从而应用能验证用户身份。

2.3.2、登录认证的流程
  1. 收集用户二点身份/凭证,及如用户名/密码
  2. 调用Subject.login进行登录,如果失败则将得到的相应的AuthenticationException异常,根据异常提示用户登录错误信息,否则登陆成功。
  3. 创建自定义的Realm类,继承org.apache.shiro.realm.AuthenticationRealm类,实现doGetAuthenticationInfo()方法

在这里插入图片描述

2.3.3、登录认证示例

创建测试类,获取认证对象,进行登录认证,如下:

public class ShiroRun {
	@SuppressWarnings("deprecation")
	public static void main(String[] args) {
		// 1、获取Shiro初始化 通过ini文件获取用户信息
		@SuppressWarnings("deprecation")
		IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
		try {
			// 通过工厂创建SecurityManager
			SecurityManager securityManager = factory.getInstance();
			SecurityUtils.setSecurityManager(securityManager);
			// 2、获取Subject对象
			Subject subject = SecurityUtils.getSubject();
			// 3、获取外部参数 通过页面获取的用户名和密码 创建token对象
			AuthenticationToken token = new UsernamePasswordToken("zhangsan", "z31");
			// 4、完成登录
			subject.login(token);
			System.out.println("登陆成功...");
		} catch (UnknownAccountException e) {
			// TODO: handle exception
			System.out.println("用户名不存在...");
		} catch (IncorrectCredentialsException e) {
			// TODO: handle exception
			System.out.println("密码错误...");
		} catch (AuthenticationException e) {
			System.out.println("登陆失败...");
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

登陆成功:

在这里插入图片描述

密码错误:

在这里插入图片描述

账户错误:

在这里插入图片描述

2.4、角色、授权

2.4.1、授权

授权:也叫做访问控制,即在应用中控制谁访问哪些资源授权中需要了解的概念:主体(Subject)资源(Resources)权限(Permission)角色(Role)

  • **主体:**访问应用的用户,用户经授权才可访问指定资源
  • **资源:**在应用中用户可以访问的URL,比如JSP页面,查看/编辑某些权限
  • **权限:**表示在应用中用户能不能访问某个资源(有没有权利去访问某一个资源)

Shiro支持粗粒度的授权(用户模块的所有权限的授权)、也支持细粒度的授权(某个模块下的某个功能,比如查询)

2.4.1.1、授权方式
  1. 编程式授权:通过IF-ELSE授权
if(subject.hasRole("admin")){
    // 有admin的权限
}else if(subject.hasRole("commons")){
	// 普通用户的权限
}else{
    // 没有权限
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. 注解式:通过执行的Java方法上加上注解完成,没有泉下今年的将抛出异常
@RequriesRole("admin")
public void queryALL(){
    // 具体的业务逻辑
}
  • 1
  • 2
  • 3
  • 4
  1. JSP/GSP标签,在JSP/GSP页面通过相应的标签完成
<shiro:hasRole name="admin">
    <input type="button" class="queryAll" name="queryAll" value="查询所有"/>
</shiro:hasRole>
  • 1
  • 2
  • 3
2.4.1.2、授权流程
  1. 首先调用Subject.isPermitted*/hasRole* 接口,其余委托SecurityManager。而SecurityManager接着会委托给Authorizer。
  2. Authorizer是真正的授权者,如果调用isPermitted(“user:view”),其首先会通过PermissionResolver把字符串转化成相应的Permission示例。
  3. 再授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限
    1. Authorizer会判断Realm的角色/权限是否与传过来的匹配,如果有多个Realm,则会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted*/hasRole*会返回true,否则返回false表示授权失败。

在这里插入图片描述

2.4.2、角色

角色:权限的集合(比如说系统管理员、业务人员、普通用户人员等)

2.4.3、角色授权示例

【角色】

在ini文件里配置用户角色的权限信息:

[users]
zhangsan=z3,admin,commons
lisi=l4,commons

[roles]
admin=user:insert,user:select
commons=user:select
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

通过以下方式完成用户角色下权限的判断:

if (subject.isPermitted("user:insert")) {
	System.out.println("用户有插入权限");
}else {
	System.out.println("没有insert权限");
}
  • 1
  • 2
  • 3
  • 4
  • 5

完整示例:

public class ShiroRun {
	@SuppressWarnings("deprecation")
	public static void main(String[] args) {
		// 1、获取Shiro初始化 通过ini文件获取用户信息
		@SuppressWarnings("deprecation")
		IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
		try {
			// 通过工厂创建SecurityManager
			SecurityManager securityManager = factory.getInstance();
			SecurityUtils.setSecurityManager(securityManager);
			// 2、获取Subject对象
			Subject subject = SecurityUtils.getSubject();
			// 3、获取外部参数 通过页面获取的用户名和密码 创建token对象
			AuthenticationToken token = new UsernamePasswordToken("lisi", "l4");
			// 4、完成登录
			subject.login(token);
			System.out.println("登陆成功...");
			// 5、判断用户角色
			if(subject.hasRole("commons")) {
				System.out.println("拥有commons角色");
			}else{
				System.out.println("没有拥有commons角色");
			}
			if (subject.isPermitted("user:insert")) {
				System.out.println("用户有插入权限");
			}else {
				System.out.println("没有insert权限");
			}
		} catch (UnknownAccountException e) {
			// TODO: handle exception
			System.out.println("用户名不存在...");
		} catch (IncorrectCredentialsException e) {
			// TODO: handle exception
			System.out.println("密码错误...");
		} catch (AuthenticationException e) {
			System.out.println("登陆失败...");
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

注意:使用subject.checkPermission(“user:insert”);没有权限则会抛异常

【授权】

首先现在ini文件里为用户添加相应的角色(zhangsan添加admin、commons角色;lisi添加commons角色)

[users]
zhangsan=z3,admin,commons
lisi=l4,commons
  • 1
  • 2
  • 3

使用以下方式判断角色:

if(subject.hasRole("commons")) {
		System.out.println("拥有commons角色");
	}else{
		System.out.println("没有拥有commons角色");
}
  • 1
  • 2
  • 3
  • 4
  • 5

完整示例:

public class ShiroRun {
	@SuppressWarnings("deprecation")
	public static void main(String[] args) {
		// 1、获取Shiro初始化 通过ini文件获取用户信息
		@SuppressWarnings("deprecation")
		IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
		try {
			// 通过工厂创建SecurityManager
			SecurityManager securityManager = factory.getInstance();
			SecurityUtils.setSecurityManager(securityManager);
			// 2、获取Subject对象
			Subject subject = SecurityUtils.getSubject();
			// 3、获取外部参数 通过页面获取的用户名和密码 创建token对象
			AuthenticationToken token = new UsernamePasswordToken("zhangsan", "z3");
			// 4、完成登录
			subject.login(token);
			System.out.println("登陆成功...");
			// 5、判断用户角色
			if(subject.hasRole("commons")) {
				System.out.println("拥有commons角色");
			}else{
				System.out.println("没有拥有commons角色");
			}
		} catch (UnknownAccountException e) {
			// TODO: handle exception
			System.out.println("用户名不存在...");
		} catch (IncorrectCredentialsException e) {
			// TODO: handle exception
			System.out.println("密码错误...");
		} catch (AuthenticationException e) {
			System.out.println("登陆失败...");
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

2.5、密码加密

在实际的开发中,一些敏感的信息需要加密,比如说用户的密码,shiro内嵌了很多的加密算法

2.5.1、使用Shiro进行加密
public class ShiroMD5 {

	public static void main(String[] args) {
		String salt = "salt";
		// 1、密码明文
		String password = "z3";
		// 2、使用MD5加密
		Md5Hash MD5 = new Md5Hash(password);
		System.out.println("使用MD5加密后的密码:" + MD5.toHex());
		// 3、给MD5加盐值 在加密玩的再次拼接一段字符串
		Md5Hash MD5_2 = new Md5Hash(password, salt);
		System.out.println("使用MD5(带盐值)加密后的密码:" + MD5_2.toHex());
		// 3、给MD5加盐值 多次加密
		Md5Hash MD5_3 = new Md5Hash(password, salt,3);
		System.out.println("使用MD5(带盐值三次加密)加密后的密码:" + MD5_3.toHex());
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

三、Shiro整合SpringBoot

3.1、整合依赖

<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">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.wei</groupId>
		<artifactId>dhcc_ShiroProject</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	<artifactId>dhcc_ShiroSpringBoot</artifactId>
	
	 <properties>
        <java.version>1.8</java.version>
        <spring.shiro.version>1.9.0</spring.shiro.version>
    </properties>
    
	<dependencies>
		<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>
        <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>${spring.shiro.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--页面模板依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--热部署依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- MyBatis-plus -->
        <dependency>
        	<groupId>com.baomidou</groupId>
        	<artifactId>mybatis-plus-boot-starter</artifactId>
        	<version>3.0.5</version>
        </dependency>
        <!-- MySQL -->
        <dependency>
        	<groupId>mysql</groupId>
        	<artifactId>mysql-connector-java</artifactId>
        	<version>8.0.28</version>
        </dependency>
	</dependencies>
	
	<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>    
        </plugins>
    </build>
</project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

3.2、yml配置文件

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/*Mapper.xml
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shirodb?characterEncoding=UTF-8&useUnicode=true&useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
shiro: 
  loginUrl: /shiroController/login
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3.3、创建目录结构

在这里插入图片描述

3.4、创建数据库

打开SqlYog工具创建数据库shirodb

USE `shirodb`;

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(50) DEFAULT NULL COMMENT '用户名',
  `password` varchar(50) DEFAULT NULL COMMENT '密码',
  `role_id` bigint DEFAULT NULL COMMENT '角色编号',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3.5、创建对应的类

【实体类User】

@Data
public class User {
	private Long id;
	private String name;
	private String password;
	private Long roleId;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

【respority数据持久层】

@Repository
public interface UserMapper extends BaseMapper<User>{}
  • 1
  • 2

采用MyBatis-PLUS的通用Mapper

【Service数据服务层(业务层)】

// 接口
public interface UserService {
	User getUserInfoByName(String name);
}

// 实现类
@Service
public class UserServiceImpl implements UserService {
	
	@Resource
	private UserMapper userMapper;

	public User getUserInfoByName(String name) {
		QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
		queryWrapper.eq("name", name);
		User user = userMapper.selectOne(queryWrapper);
		return user;
	}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

【controller控制层】

@Controller
@RequestMapping("/shiro")
public class UserController {

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	@ResponseBody
	public String login(@RequestParam("name") String name, @RequestParam("password") String password) {
		// 1、获取Subject对象
		Subject subject = SecurityUtils.getSubject();
		// 2、 封装请求对象到Token对象
		UsernamePasswordToken token = new UsernamePasswordToken(name,password);
		try {
			// 3、调用Subject的login方法完成登录
			subject.login(token);
			return "登陆成功!";
		} catch (UnknownAccountException e) {
			e.printStackTrace();
			System.out.println("用户名不存在...");
			return "用户名错误,登陆失败";
		} catch (IncorrectCredentialsException e) {
			e.printStackTrace();
			System.out.println("密码错误...");
			return "密码错误,登陆失败!";
		} catch (AuthenticationException e) {
			e.printStackTrace();
			System.out.println("登陆失败...");
			return "登陆失败";
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

【Shiro的自定义授权配置类】

@Component
public class MyShiroRealm extends AuthorizingRealm {

	@Resource
	private UserService UserService;

	/**
	 * 用户的登录信息 自定义授权方法
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 自定义登录认证方法 token
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// 获取登录用户的信息
		String name = token.getPrincipal().toString(); // 获取用户名
//		String password = new String((char[]) token.getCredentials()); // 获取密码
		// 调用业务层的接口获取数据库的用户信息
		User user = UserService.getUserInfoByName(name);
		if (user != null) {
			// 封装数据
			AuthenticationInfo info = new SimpleAuthenticationInfo(
									token.getPrincipal().toString(),
									user.getPassword(),
									ByteSource.Util.bytes("salt"),
									token.getPrincipal().toString()
					);
			return info;
		}
		return null;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

这里需要继承一下AuthorizingRealm,重写参数为token的方法,实现用户的授权登录功能

3.6、创建Shiro配置类

3.6.1、自定义Shiro配置类

要想实现自定义的Shiro配置类,需要创建一个DefaultSecurityManager的方法,在里面去重新自定义授权功能。

@Configuration
public class ShiroConfig {

	@Resource
	private MyShiroRealm shiroRealm;
	
	/**
	* 描述:TODO(这里用一句话描述这个方法的作用) 
	* @Title: 创建默认的安全管理器
	* @return
	* @author weiyongpeng
	* @date  2022年10月3日 上午8:15:52
	 */
	@Bean
	public DefaultWebSecurityManager defaultWebSecurityManager() {
		// 1、创建DefaultWebSecurityManager
		DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
		// 2、创建加密对象 设置加密属性
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
		// 3、将加密对象存储到MyShiroRealm 采用MD5 迭代次数3
		matcher.setHashAlgorithmName("MD5");
		matcher.setHashIterations(3);
		shiroRealm.setCredentialsMatcher(matcher);
		// 4将MyShiroRealm存储到DefaultSecurityManager
		manager.setRealm(shiroRealm);
		// 5、返回DefaultSecurityManager
		return manager;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
3.6.2、自定义Shiro拦截范围
@Bean
public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() {
	DefaultShiroFilterChainDefinition filter = new DefaultShiroFilterChainDefinition();
	// 设置不忍证可以访问的资源
	filter.addPathDefinition("/shiro/login", "anon");
	filter.addPathDefinition("/login","anon");
	// 设置需要进行登录才可以访问的拦截范围
	filter.addPathDefinition("/**", "authc");
	return filter;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.7、测试

使用APIFOX测试登录

【登陆成功】

在这里插入图片描述

【登陆失败】

在这里插入图片描述

3.8、登录认证前端

使用Thymeleaf实现前端的登陆页面

【引入依赖】

<!--页面模板依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

【添加配置】

  thymeleaf:
    cache: false
    prefix: classpath:/templates/
    suffix: .html
    encoding: UTF-8
    mode: HTML5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

【编写页面】

登陆页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
	<h1>Shiro登录认证</h1>
	<form th:action="@{/shiro/userLogin}" method="post">
		<div>
			<label>用户名:</label> <input type="text" name="name"
				placeholder="请输入用户名:">
		</div>
		<div>
			<label>用户名:</label> <input type="password" name="password"
				placeholder="123456">
		</div>
		<div class="buttonDiv">
			<input type="reset" value="重置"> 
			<input type="submit" value="登录">
		</div>
	</form>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

登陆成功首页

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录首页</title>
</head>
<body>
登陆的用户:<span th:text="${session.user}"></span>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

登陆失败错误页

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>登陆失败错误页面</h1>
<h3>
	对不起,你在登陆的时候遇到了<span th:text="${errorMsg}"></span>的错误
	<a th:href="@{/shiro/login}">重新登陆</a>
</h3>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

修改controller的代码

@RequestMapping(value = "/login",method = RequestMethod.GET)
	public String login() {
		return "login";
	}
	
	@RequestMapping(value = "/userLogin", method = RequestMethod.POST)
	public String userLogin(@RequestParam("name") String name, 
							@RequestParam("password") String password,
							HttpSession session,
							Model model) {
		// 1、获取Subject对象
		Subject subject = SecurityUtils.getSubject();
		// 2、 封装请求对象到Token对象
		UsernamePasswordToken token = new UsernamePasswordToken(name,password);
		try {
			// 3、调用Subject的login方法完成登录
			subject.login(token);
			// 放入session
			session.setAttribute("user", token.getPrincipal().toString());
			return "main";
		} catch (UnknownAccountException e) {
			e.printStackTrace();
			System.out.println("用户名不存在...");
			model.addAttribute("errorMsg","用户名错误,登陆失败");
			return "error";
		} catch (IncorrectCredentialsException e) {
			e.printStackTrace();
			System.out.println("密码错误...");
			model.addAttribute("errorMsg","密码错误,登陆失败!");
			return "error";
		} catch (AuthenticationException e) {
			e.printStackTrace();
			System.out.println("登陆失败...");
			model.addAttribute("errorMsg","登陆异常,登陆失败!");
			return "error";
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

四、多个Realm登录校验

  1. 多个Realm实现原理

当应用程序配置多个Realm时,例如,用户名密码校验,手机号校验,邮箱校验等等。Shiro的ModularRealmAuthentication会使用内部的AuthenticationStarategy组件判断认证是否成功或者谁败。

AuthenticationStrategy是一个无状态的组件,它本身验证尝试中被询问4次(这4次交互所需的任何必须的状态将被作为方法参数)

(1)在所有的Realm被调用之前

(2)在调用Realm的getAuthenticationInfo()方法之前

(3)在调用Realm的getAuthenticationInfo()方法之后

(4)在所有的Realm被调用之后

五、rememberMe功能

Shiro提供了记住我的(Remember Me)功能,用户可以在登陆成功后,下次访问页面无需再次登录仍然可以访问。

5.1、基本流程

  1. 首先在登陆的页面选中Remember Me然后再登陆成功后,如果是浏览器登录,一般会把Remember Me的Cookie写道客户端并保存。
  2. 关闭浏览器再次重新打开,会发现浏览器还是记住你。
  3. 访问一般的网页服务器,仍然知道你是谁,且能正常访问。
  4. 但是,如果我们访问电商平台,如果要查看我的订单或者进行支付,此事还需再次进行身份的认证。

5.2、代码实现

5.2.1、设置记住我

在配置类里的安全管理器方法里添加记住我功能

在这里插入图片描述

5.2.2、配置记住我管理器以及Cookie属性
// Cookie的属性设置
public SimpleCookie rememberCookie() {
	SimpleCookie cookie = new SimpleCookie("rememberMe");
	// 设置跨域
//	cookie.setDomain(domain);
	cookie.setPath("/");
	cookie.setHttpOnly(true);
	cookie.setMaxAge(30*24*60*60); // 30天
	return cookie;
}
	
//创建CookieMaanger
public CookieRememberMeManager rememberMeManager() {
	CookieRememberMeManager manager  =new CookieRememberMeManager();
	manager.setCookie(rememberCookie());
	manager.setCipherKey("1234567890987654".getBytes());
	return manager;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
5.2.3、添加用户过滤器

保证在登陆成功后,Shiro将登陆成功的用户信息放入到cookie中存储

在这里插入图片描述

5.2.4、改造Controller登录接口
// 2、 封装请求对象到Token对象 开启Remember
		UsernamePasswordToken token = new UsernamePasswordToken(name,password,rememberMe);
  • 1
  • 2
5.2.5、改造登陆页面
<div>记住我:<input type="checkbox" name="rememberMe" value="true"> </div>
  • 1

在这里插入图片描述

六、用户登出

用户登陆之后,配套的操作有登出操作,直接通过Shiro过滤器即可以实现

6.1、代码实现

【过滤器】

@Bean
	public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() {
		DefaultShiroFilterChainDefinition filter = new DefaultShiroFilterChainDefinition();
		// 设置不认证可以访问的资源
		filter.addPathDefinition("/shiro/userLogin", "anon");
		filter.addPathDefinition("/shiro/login","anon");
		// 配置登出操作
		filter.addPathDefinition("/logout", "logout");
		// 设置需要进行登录认证才可以访问的拦截范围
		filter.addPathDefinition("/**", "authc");
		// 添加remember的用户
		filter.addPathDefinition("/**", "user");
		return filter;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

【登陆后的页面改造】

在这里插入图片描述

七、授权、角色认证

7.1、角色认证

用户登录后,需要验证是否具有指定角色权限,Shiro也提供了方便的工具进行判断,

这个工具就是Realm的doGetAuthenticationinfo方法进行判断,出发权限判断的有两种方式

  1. 在页面中通过shiro:xxxx属性判断
  2. 在接口中通过注解@Requiresxxxxx判断
7.1.1、后端接口服务注解
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/570314
推荐阅读
相关标签