Shiro

一、初识shiro

1.shiro简介:

官网
在这里插入图片描述
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解API,你可以快速、轻松地获取任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序

2.框架图说明:

官网架构说明

3.从外部查看shiro框架

在这里插入图片描述
应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject

api说明
Subject主体,代表当前‘用户’ 。这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委派给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者
Shiro SecurityManager安全管理器;即所有与安全有关的操作都会与SecurityManager交互且它管理者所有Subject;可以看出它是Shiro的核心,它负责与后面介绍的其它组件进行交互,可以把它看成DispathcherServlet前端控制器
Realm域,Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

4.内部结构框架

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

5.常见单词说明

在这里插入图片描述

6.Shiro中的shiro.ini说明:

在这里插入图片描述

(1) 、main

提供了对根对象securityManager及其依赖对象的配置

#创建对象
securityManager=org.apache.shiro.mgt.DefaultSecurityManager 

其构造器必须是public空参构造器,通过反射创建相应的实例。
1.对象名=全限定类名 相当于调用public无参构造器创建对象
2.对象名.属性名=值 相当于调用于setter方法设置常量值
3.对象名.属性名=$对象引用 相当于调用setter方法设置对象引用

(2)、users

提供了对用户/密码及其角色的配置,用户名=密码,角色1,角色2
username=password,role1,role2
  例如:配置用户名/密码及其角色,格式:“用户名=密码,角色1,角色2”,角色部分可省略。如:

[users] 
zhang=123,role1,role2 
wang=123 
(3)、roles

提供了角色及权限之间关系的配置,角色=权限1,权限2 role1 = permission1 , permission2
  例如:配置角色及权限之间的关系,格式:“角色=权限1,权限2”;如:

[roles] 
role1=user:create,user:update 
role2=*  

(4)、urls

用于web,提供了对web url拦截相关的配置,url=拦截器[参数],拦截器

/index.html = anon 
/admin/** = authc, roles[admin],perms["permission1"]

7.第一个案例:

①项目总体图:

在这里插入图片描述

添加依赖:
<dependencies>
  <dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-core</artifactId>
	<version>1.1.0</version>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-simple</artifactId>
	<version>1.6.1</version>
	<scope>test</scope>
</dependency>

<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.12</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency>
</dependencies>
②添加shiro.ini文件
[users]
root=123456
# 账号为root 密码是123456
③认证操作:
package com.sxt.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

/**
 * @author Administrator
 *shiro的第一个入门案例
 */
public class HelloTest {
	public static void main(String[] args) {
		//1.加载配置文件获取Factory对象
		Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
		//2.获取SecurityManager对象
		SecurityManager securityManager =factory.getInstance();
		//3.将SecurityManager添加到系统
		SecurityUtils.setSecurityManager(securityManager);
		//4.通过SecurityManager获取Subject对象
		Subject subject=SecurityUtils.getSubject();
		//账号密码是客户端提交的数据
		AuthenticationToken token=new UsernamePasswordToken("root","123456");
		//5.实现认证操作
		try{
			subject.login(token);
			System.out.println("认证成功");
		}catch(UnknownAccountException e){
			System.out.println("账号输入错误。,,,");
		}catch (IncorrectCredentialsException e) {
			System.out.println("密码输入错误。。。");
		}
		
	}
}

④实现:

在这里插入图片描述

二、自定义Realm

通过上面我们发现仅仅将数据信息定义在ini文件中我们实际开发环境有很大不兼容,所以我们希望能够自己定义Realm。

1.自定义Realm的实现

(1)、创建自定义Realmjava类

创建一个java文件继承AuthorizingRealm类,重写两个抽象方法:

package com.sxt.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * @author Administrator
 * 定义的Realm
 */
public class SecurityRealm  extends AuthorizingRealm{

	/**
	 * 认证的方法
	 * 就是我们在测试代码中 定义的UserPassWoldToken对象
	 * 有我们保存的需要验证的账号密码信息
	 */

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken t=(UsernamePasswordToken) token;
		//获取登录的账号
		String username=t.getUsername();
		System.out.println("登录的账号:"+username);
		//通过jdbc去数据库中查询该账号对应的记录
		if(!"root".equals(username)){
			//账号不存在
			return null;
		}
		//数据库中查询的密码是123456
		String password="123456";
		//身份信息(可以是账号也可以是对象) 密码 realmName(自定义)
		return new SimpleAuthenticationInfo(username,password,"tang");
	}
	
	/**
	 * 授权方法
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		// TODO Auto-generated method stub
		return null;
	}
}

方法名说明
doGetAuthentictionInfo完成账号认证的方法
doGetAuthorizationInfo完成用户授权的方法

(2)、配置ini.xml文件:

[main]
#自定义 realm
customRealm=com.sxt.realm.SecurityRealm
#将realm设置到securityManager
securityManager.realms=$customRealm

(3)、测试(代码跟上面的一样)

package com.sxt.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

/**
 * @author Administrator
 *shiro的第一个入门案例
 */
public class HelloTest {
	public static void main(String[] args) {
		//1.加载配置文件获取Factory对象
		Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
		//2.获取SecurityManager对象
		SecurityManager securityManager =factory.getInstance();
		//3.将SecurityManager添加到系统
		SecurityUtils.setSecurityManager(securityManager);
		//4.通过SecurityManager获取Subject对象
		Subject subject=SecurityUtils.getSubject();
		//账号密码是客户端提交的数据
		AuthenticationToken token=new UsernamePasswordToken("root","123456");
		//5.实现认证操作
		try{
			subject.login(token);
			System.out.println("认证成功");
		}catch(UnknownAccountException e){
			System.out.println("账号输入错误。,,,");
		}catch (IncorrectCredentialsException e) {
			System.out.println("密码输入错误。。。");
		}
		
	}
}

在这里插入图片描述

2.原理分析

为什么要继承AuthorizingRealm?

通过分析认证的流程,我们发现在认证的过程中核心代码是:
在这里插入图片描述
核心方法是doGetAuthenticationInfo(token)
在Realm的结构中
在这里插入图片描述
AuthorizingRealm和AuthenticatingRealm都提供的有doGetAuthenticationInfo(token)的抽象方法。但是AuthenticatingRealm中要重写的抽象方法太多而AuthorizingRealm只需要重写两个方法,且这两个方法都是我们需要使用的。故选择继承AuthorizingRealm

自定义的Realm什么时候被调用的?

在这里插入图片描述

密码验证什么时候执行的?

注意:自定义Realm中只完成了账号的认证。密码认证还是在AuthenticatingRealm中完成的,只是我们在自定义Realm中完成了密码的设置。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、shiro-加密

加密,是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使获得了已加密的信息,但因不知解密的方法,任然无法了解信息的内容

1、概念:

数据加密的基本过程就是对原来为明文的文件或数据按照某种算法进行处理,使其成为不可读的一段代码,通常称为“密文”,使其只能在输入相应的密匙之后才能显示出本来内容,通过这样的途径来达到保护数据不被非法人窃取、阅读的目的。该过程的逆过程为解密,即将该编码信息转换为其原来数据的过程。

2、加密分类:

(1)、对称加密

双方使用的同一个密匙,既可以加密又可以解密,这种加密方法称为对称加密,也称单密匙加密。

(2)、非对称加密

一对密匙由公钥和私钥组成(可以使用很多对密匙)。私钥解密公钥加密数据,公钥解密私钥加密数据(私钥公钥可以互相加密解密)。

3、加密算法分类

(1)、单项加密

单项加密是不可逆的,也就是只能加密,不能解密。通常用来传输类型用户名和密码,直接将加密后的数据提交到后台,因为后台不需要知道用户名和密码,可以直接将接收到的加密后的数据存储到数据库

(2)、双向加密

通常分为对称性加密算法和非对称性加密算法,对于对称性加密算法,信息接收双方都需事先知道密匙和加解密算法且其密匙是相同的,之后便是对数据进行 加解密了。非对称算法与之不同,发送双方A,B事先均生成一堆密匙,然后A将自己的公有密匙发送给B,B将自己的公有密匙发送给A,如果A要给B发送消 息,则先需要用B的公有密匙进行消息加密,然后发送给B端,此时B端再用自己的私有密匙进行消息解密,B向A发送消息时为同样的道理。

4、常见的算法

在这里插入图片描述

MD5的使用:
package com.sxt.test;

import org.apache.shiro.crypto.hash.Md5Hash;

public class Md5HashTest {
	public static void main(String[] args) {
		// 对单个信息加密
		Md5Hash md5 = new Md5Hash("123456");
		System.out.println(md5.toString());
		// 加密添加盐值 增大解密难度
		md5 = new Md5Hash("123456","aaa");
		System.out.println(md5.toString());
		// 加密添加盐值 增大解密难度  迭代1024次
		md5 = new Md5Hash("123456","aaa",1024);
		System.out.println(md5);
	
	}
}

输出的结果:
在这里插入图片描述

盐值的作用:

使用MD5存在一个问题,相同的password生成的hash值是相同的,如果两个用户设置了相同的密码,那么数据库中会存储两个相同的值,这是极不安全的,加Salt可以在一定程度上解决这一问题,所谓的加Salt方法,就是加点‘佐料’。其基本想法是这样的,当用户首次提供密码时(通常是注册时)由系统自动往这个密码里撒一些‘佐料’,然后在散列,而当用户登录时,系统为用户提供的代码上撒上相同的‘佐料’,然后散列,再比较散列值,来确定密码是否正确。
加盐的原理:
给原文加入随机数生成新的MD5的值

shiro中使用MD5加密

1.认证方法中修改
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
	// 获取账号信息
	String principal = (String) token.getPrincipal();
	// 正常逻辑此处应该根据账号去数据库中查询,此处我们默认账号为 root 密码123456
	// 验证账号
	if(!"root".equals(principal)){
		// 账号错误
		return null;
	}
	//String pwd = "123456";
	// 12345 根据 盐值 aaa 加密获取的密文
	//88316675d7882e3fdbe066000273842c  1次迭代的密文
	//a7cf41c6537065fe724cc9980f8b5635  2次迭代的密文
	String pwd = "88316675d7882e3fdbe066000273842c";
	// 验证密码
	AuthenticationInfo info = new SimpleAuthenticationInfo(
			principal, pwd,new SimpleByteSource("aaa"),"myrealm");
	return info;
}

在这里插入图片描述

2.ini.xml文件的修改:
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1

#将凭证匹配器设置到realm
customRealm=com.dpb.realm.MyRealm
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm

3.测试:
@Test
public void test() {
	// 1.获取SecurityManager工厂对象
	Factory<SecurityManager> factory = 
			new IniSecurityManagerFactory("classpath:shiro.ini");
	// 2.通过Factory对象获取SecurityManager对象
	SecurityManager securityManager = factory.getInstance();
	// 3.将SecurityManager对象添加到当前运行环境中
	SecurityUtils.setSecurityManager(securityManager);
	
	// 4.获取Subject对象
	Subject subject = SecurityUtils.getSubject();
	AuthenticationToken token = new UsernamePasswordToken("root", "123456");
	// 登录操作
	try {
		subject.login(token);
	} catch (UnknownAccountException e) {
		System.out.println("账号出错...");
	} catch(IncorrectCredentialsException e){
		System.out.println("密码出错...");
	}
	// 获取登录的状态
	System.out.println(subject.isAuthenticated());
}

4.迭代次数:

在这里插入图片描述
A
完成。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值