JAVAEE安全框架之shiro第一课
Posted AAA教育张晨光
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVAEE安全框架之shiro第一课相关的知识,希望对你有一定的参考价值。
1,课程回顾
2,本章重点
什么是shiro,为什么要使用
核心概念(组件)
简单入门
自定义realm用法(加密,加盐)
jdbc realm用法(使用数据库)
3,具体内容
3.1 什么是shiro
[http://shiro.apache.org/](http://shiro.apache.org/)
**Apache Shiro™(trademark)**是一个功能强大且易于使用的Java安全框架,用于执行身份验证,授权,加密和会话管理。使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大的Web和企业应用程序。
3.2 为什么要使用shiro(和它同级spring security)
**易于使用** -易于使用是该项目的最终目标。应用程序安全性可能非常令人困惑和沮丧,并被视为“必要的邪恶”。如果您使它易于使用,以使新手程序员可以开始使用它,那么就不必再痛苦了。
**全面**-Apache Shiro声称没有其他具有范围广度的安全框架,因此它很可能是满足安全需求的“一站式服务”。
**灵活**-Apache Shiro可以在任何应用程序环境中工作。尽管它可以在Web,EJB和IoC环境中运行,但并不需要它们。Shiro也不要求任何规范,甚至没有很多依赖性。
**具有Web功能 -**Apache Shiro具有出色的Web应用程序支持,允许您基于应用程序URL和Web协议(例如REST)创建灵活的安全策略,同时还提供一组JSP库来控制页面输出。
**可插拔** -Shiro干净的API和设计模式使它易于与许多其他框架和应用程序集成。您会看到Shiro与Spring,Grails,Wicket,Tapestry,Mule,Apache Camel,Vaadin等框架无缝集成。
**支持** -Apache Shiro是Apache Software Foundation(Apache软件基金会)的一部分,该组织被证明以其社区的最大利益行事。项目开发和用户群体友好的公民随时可以提供帮助。如果需要,像Katasoft这样的商业公司也可以提供专业的支持和服务。
3.3 核心概念
3.3.1 Subject
Subject一词是一个安全术语,基本上表示“当前正在执行的用户”。它只是意味着“当前正在与软件交互的东西”。它只是不被称为“用户”,因为“用户”一词通常与人类相关联。在安全的世界,术语“主题”可以指一个人,但也有会谈进程,守护进程帐户,或任何类似。
import org.apache.shiro.subject.Subject;
import org.apache.shiro.SecurityUtils;
Subject currentUser = SecurityUtils.getSubject();
3.3.2 SecurityManager
主题的“幕后”对应对象是SecurityManager。主题代表当前用户的安全操作,而SecurityManager管理*所有*用户的安全操作。它是Shiro体系结构的核心,并充当一种“伞”对象,引用了许多内部嵌套的安全组件,这些安全组件构成了一个对象图。它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。每个应用程序几乎总是有一个SecurityManager实例。它本质上是一个应用程序单例(尽管不必是*静态*单例)普通的Java代码,Spring XML,YAML,.properties和.ini文件等进行配置
ini文件方式,实例化SecurityManager 配置:
[main]
#CredentialsMatcher 认证匹配器
cm = org.apache.shiro.authc.credential.HashedCredentialsMatcher
cm.hashAlgorithm = SHA-512
cm.hashIterations = 1024
Base64 encoding (less text):
在这里插入代码片`cm.storedCredentialsHexEncoded = false
[users]
jdoe = TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJpcyByZWFzb2
asmith = IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbXNoZWQsIG5vdCB
[roles]
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.util.Factory;
// 1。加载INI配置
Factory <SecurityManager> factory =
new IniSecurityManagerFactory(“ classpath:shiro.ini”);
// 2。创建SecurityManager
SecurityManager securityManager = factory.getInstance();
// 3。使它可访问
SecurityUtils.setSecurityManager(securityManager);`
3.3.3 Realms
Realms充当Shiro与应用程序的安全数据之间的“桥梁”或“连接器”,也就是说,当需要真正与安全性相关的数据(例如用户帐户)进行交互以执行身份验证(登录)和授权(访问控制)时,Shiro会从为应用程序配置的一个或多个Realms中查找其中的许多内容。本质上是特定于安全性的DAO:它封装了数据源的连接详细信息,并根据需要使关联数据可用于Shiro。
#数据源
dataSource=org.apache.commons.dbcp.BasicDataSource
#数据源链接要素
dataSource.driverClassName=oracle.jdbc.driver.OracleDriver
dataSource.url=jdbc:oracle:thin:@localhost:1521:orcl
dataSource.username=scott
dataSource.password=tiger
#jdbcrealm
jdbcrealm=org.apache.shiro.realm.jdbc.JdbcRealm
#jdbcrealm需要用到的数据源
jdbcrealm.dataSource=$dataSource
#开启查找权限
jdbcrealm.permissionsLookupEnabled=true
#设置安全管理器使用的jdbcrealm
securityManager.realms=$jdbcrealm
3.4 认证和授权
3.4.1 认证(Authentication):
身份验证是验证用户身份的过程。也就是说,当用户通过应用程序进行身份验证时,他们在证明自己实际上就是他们所说的身份。有时也称为“登录”。这通常是一个三步过程。
- 收集用户的标识信息(称为主体**用户名)和支持身份的凭证(称为凭据密码)。
AuthenticationToken token =
new UsernamePasswordToken(username, password);
2,将主体和凭据提交到系统。
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
3,如果提交的凭据与系统对该用户身份的期望匹配,则认为该用户已通过身份验证。如果它们不匹配,则不认为用户已通过身份验证。
try
currentUser.login(token);
//通过身份验证,执行其他业务
catch (IncorrectCredentialsException ice)
//密码错误
catch (LockedAccountException lae)
//账户锁定(默认系统不提供,可以编写代码过程中自己实现)
… //许多其他异常
catch (AuthenticationException ae)
//上面配置的异常的父类
3.4.2 授权(authorization)
授权本质上是访问控制-控制用户可以在应用程序中访问的内容(例如资源,网页等)。大多数用户通过使用角色和权限等概念来执行访问控制。也就是说,通常根据分配给他们的角色和/或权限,允许用户执行某项操作或不执行某项操作。
判断角色:
if ( subject.hasRole(“administrator”) )
//show the ‘Create User’ button
else
//grey-out the button?
判断权限:
if ( subject.isPermitted(“user:create”) )
//show the ‘Create User’ button
else
//grey-out the button?
3.5 入门示例
3.5.1, 创建项目,引入jar(日志包必须加,要不然报错)
```xml
<!-- shiro jar -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.8.0</version>
</dependency>
<!-- commons-logging 日志包-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.0.4</version>
</dependency>
### 3.5.**2, 创建ini文件(shiro初始化文件),复制下面内容:**
```plain
#用户对象
[users]
#用户名密码, 拥有的角色,可以配置多个
admin=123456,role2,role3,国家军委主席
scott=tiger,role1
smith=tiger,role2
#角色
[roles]
#角色role1对用户有create、update权限
role1=user:create,user:update
#角色role2对用户有create、delete权限
role2=user:create,user:delete,dept:create
#角色role3对用户有create权限
role3=user:create
#添加的新角色 角色名称=该角色对应的权限(逗号隔开,可以多个)
r1=order:create,order:update
国家军委主席=攻打小日本,攻打韩国棒子
国家军委主席1=攻打印度阿三
3.5.3,创建java类,加载shiro.ini 实例化SecurityManager 获取subject ,进行认证和授权的操作
package com.aaa.shiro.demo;
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.subject.Subject;
import org.apache.shiro.util.Factory;
import javax.security.auth.login.AccountLockedException;
import java.util.Arrays;
/**
* @ fileName:ShiroDemoA
* @ description:
* @ author:zhz
* @ createTime:2022/1/18 10:34
* @ version:1.0.0
*/
public class ShiroDemoA
public static void main(String[] args)
//1 加载ini配置,初始化工厂类
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro.ini");
//2, 创建SecurityManager
SecurityManager securityManager = factory.getInstance();
//3,使用SecurityManager可以访问 激活SecurityManager
SecurityUtils.setSecurityManager(securityManager);
//认证过程
//1. 获取提交的主体和凭据(收集用户名和密码)
AuthenticationToken authenticationToken =
new UsernamePasswordToken("scott", "tiger");
//2,Get the current Subject: 获取当前主题(通常指当前用户)
Subject currentUser = SecurityUtils.getSubject();
try
//3, Login:登录
currentUser.login(authenticationToken);
System.out.println("用户登录(认证,验证)成功!!!");
//上一句代码如果没有异常说明认证成功
// 授权过程
//1 角色判断
if(currentUser.hasRole("role2"))
System.out.println("拥有role2角色,可以执行用户创建和删除。。。");
if(currentUser.hasRole("role1"))
System.out.println("拥有role1角色,可以用户创建和更新");
if(currentUser.hasRole("国家军委主席"))
System.out.println("拥有国家军委主席角色,可以执行攻打小日本,攻打韩国棒子。。。");
if(currentUser.hasRole("国家军委主席1"))
System.out.println("没有有国家军委主席1角色,可以执行攻打印度阿三。。。");
//2 权限判断
if(currentUser.isPermitted("user:create"))
System.out.println("当前用户拥有用户创建权限,可以进行用户创建");
if(currentUser.isPermitted("dept:create"))
System.out.println("当前用户拥有部门创建权限,可以进行部门创建");
if(currentUser.isPermitted("dept:update"))
System.out.println("当前用户没有有部门更新权限,不可以进行部门更新");
//授权
//判断角色
System.out.println("测试scott是否有用role1角色:"+currentUser.hasRole("role1"));//true
System.out.println("测试scott是否有用role2角色:"+currentUser.hasRole("role2"));//false
// hasAllRoles拥有为true
System.out.println("测试scott是否有用role1,role2角色:"+
currentUser.hasAllRoles(Arrays.asList("role1","role2")));//false
//判断权限
System.out.println("测试scott是否拥有user:create权限:"+
currentUser.isPermitted("user:create"));//true
System.out.println("测试scott是否拥有dept:del权限:"+
currentUser.isPermitted("dept:del"));//true
System.out.println("测试scott是否拥有dept:update权限:"+
currentUser.isPermitted("dept:update"));
System.out.println("测试scott是否拥有多个权限1:"+
currentUser.isPermittedAll("user:create","user:update"));//true
System.out.println("测试scott是否拥有多个权限2:"+
currentUser.isPermittedAll("dept:update","dept:create"));//false
//用户登出
currentUser.logout();
catch (AccountException e)
//e.printStackTrace();
System.out.println("用户名错误!!!");
catch (IncorrectCredentialsException e)
// e.printStackTrace();
System.out.println("密码错误!!!");
catch (AuthenticationException e)
// e.printStackTrace();
System.out.println("用户名或者密码错误!!!");
3.6 自定义realm用法 (加密,加盐)
3.6.1,创建自定义的ini文件 shiro_custom_realm.ini 复制下面内容
[main]
#引入加密类 cm CredentialsMatcher 认证匹配器
cm = org.apache.shiro.authc.credential.HashedCredentialsMatcher
#规定加密方式 hash哈希 AlgorithmName 算法名称
cm.hashAlgorithmName = SHA-512
#哈希次数
cm.hashIterations = 10
Base64 encoding (less text):如果credential 哈希是Hex 编码的话为true,base64的话为false
#cm.storedCredentialsHexEncoded = false
#配置自定义realm
myRealm=com.aaa.shiro.util.MyCustomRealm
$cm 引用上面cm配置
myRealm.credentialsMatcher = $cm
#把安全数据交给SecurityManager认证或者授权
securityManager.realms=$myRealm
3.6.2,编写自定义MyRealm类继承AuthorizingRealm类,重写认证和授权方法
//需要重写的授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
return null;
//需要重写的认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException
// 获取已经收集用户的标识信息中的用户名
Object userName = authenticationToken.getPrincipal();
//模拟调用编写的服务类获取用户信息
// UserInfo userInfo = userService.getUserInfo(userName);
String userNameByDB = "scott";
//判断是否匹配
if(!userNameByDB.equals(userName))
//账号不配置,直接抛出账户异常
throw new AccountException();
//加密过的密码
//tiger 经过加盐(123456)后10次hash结果
String myDBPassWord ="5cf729699270e380bfc886aedc3fe8f2acf13751c938ac2e46d281510eedf77242b6cd8e5013d505b072fa63bcaa3b7bcd593fce1e6d5e5a5487a0cee33b5fd2";
//盐 从数据库中取出的
String myDBSalt="1234567";
//实例化返回值
//SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName)
// 参数1 principal 从数据库中取出的用户信息userInfo
// 参数2 hashedCredentials 加密过的密码
// 参数3 credentialsSalt 盐值 为了提高密码安全度
//参数4 realmName 获取当前操作类的名称MyCustomRealm
AuthenticationInfo authenticationInfo =
new SimpleAuthenticationInfo(userNameByDB,myDBPassWord, ByteSource.Util.bytes(myDBSalt),getName());
return authenticationInfo;
3.6.3,编写测试类加载 shiro_custom_realm.ini调用MyCustomRealm,完成加密加盐的认证
//加载配置文件,生成工厂类
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro_custom_realm.ini");
// 使用工厂获取SecurityManager
SecurityManager securityManager = factory.getInstance();
// 使SecurityManager可以访问
SecurityUtils.setSecurityManager(securityManager);
//获取Subject(当前用户)
Subject currentUser = SecurityUtils.getSubject();
//判断是否已经认证
if(!currentUser.isAuthenticated())
//收集用户用户名和密码数据 多态
AuthenticationToken authenticationToken =
new UsernamePasswordToken("scott","tiger");
//提交收集到信息,让securityManager进行认证
try
currentUser.login(authenticationToken);
System.out.println("登录成功");
catch (AccountException e)
System.out.println("用户名错误");
catch (IncorrectCredentialsException e)
System.out.println("密码错误");
catch (AuthenticationException e)
System.out.println("用户名或者密码错误!");
//登出
currentUser.logout();
模拟注册时生成密码的工具类:
//Sha512Hash(Object source, Object salt, int hashIterations)
//参数1 原始密码
//参数2 盐值
//参数3 hash次数
String registerSalt="123456";
Sha512Hash sha512Hash= new Sha512Hash("tiger",registerSalt,
10);
// 打印出 tiger 经过加盐(123456)后10次hash结果
System.out.println(sha512Hash.toString());
// 5cf729699270e380bfc886aedc3fe8f2acf13751c938ac2e46d281510eedf77242b6cd8e5013d505b072fa63bcaa3b7bcd593fce1e6d5e5a5487a0cee33b5fd2
//存入数据库
3.7 jdbcRealm的用法
3.7.1,新加,引入连接池(dbcp,druid)和数据库驱动包(oracle和mysql)
[http://mvnrepository.com/](http://mvnrepository.com/)
<!-- [https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp](https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp) -->
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
<groupId>oracle</groupId>
<artifactId>oracle-jdbc</artifactId>
<version>12.1.0.2</version>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
3.7.2 ,查看org.apache.shiro.realm.jdbc.JdbcRealm的源码,创建数据库
oracle:
-- 用户表users
create table users(
username varchar2(20),
password varchar2(50),
age number(3)
);
-- 添加数据
insert into users values('scott','tiger',10);
insert into users values('admin','tiger',20);
insert into users values('smith','tiger',30);
-- 用户角色关联表 user_roles
create table user_roles(
role_name varchar2(20),
username varchar2(20)
);
-- 添加数据
insert into user_roles values('role1','scott');
insert into user_roles values('role2','scott');
insert into user_roles values('role2','admin');
insert into user_roles values('role3','smith');
-- 角色权限关联表 roles_permissions
create table roles_permissions(
role_name varchar2(20),
permission varchar2(100)
);
-- 添加数据
insert into roles_permissions values('role1','dept:select');
insert into roles_permissions values('role1','dept:update');
insert into roles_permissions values('role1','dept:delete');
insert into roles_permissions values('role2','user:select');
insert into roles_permissions values('role2','user:update');
insert into roles_permissions values('role3','user:delete');
-- 检查
select * from users;
select * from user_roles;
select * from roles_permissions;
**mysql:**
-- 用户表users
create table users(
username varchar(20),
password varchar(50),
age int(3)
);
-- 添加数据
insert into users values('scott','tiger',10);
insert into users values('admin','tiger',20);
insert into users values('smith','tiger',30);
-- 用户角色关联表 user_roles
create table user_roles(
role_name varchar(20),
username varchar(20)
);
-- 添加数据
insert into user_roles values('role1','scott');
insert into user_roles values('role2','scott');
insert into user_roles values('role2','admin');
insert into user_roles values('role3','smith');
-- 角色权限关联表 roles_permissions
create table roles_permissions(
role_name varchar(20),
permission varchar(100)
);
-- 添加数据
insert into roles_permissions values('role1','dept:select');
insert into roles_permissions values('role1','dept:update');
insert into roles_permissions values('role1','dept:delete');
insert into roles_permissions values('role2','user:select');
insert into roles_permissions values('role2','user:update');
insert into roles_permissions values('role3','user:delete');
-- 检查
select * from users;
select * from user_roles;
select * from roles_permissions;
3.7.3 ,编写shiro_jdbcrealm_oracle.ini shiro_jdbcrealm_mysql.ini
shiro_jdbcrealm_oracle.ini (oracle+dbcp)
#数据源
dataSource=org.apache.commons.dbcp.BasicDataSource
#数据源链接要素
dataSource.driverClassName=oracle.jdbc.driver.OracleDriver
#mysql 3306 sqlserver 1433
dataSource.url=jdbc:oracle:thin:@localhost:1521:orcl
dataSource.username=scott
dataSource.password=tiger
#jdbcrealm
jdbcrealm=org.apache.shiro.realm.jdbc.JdbcRealm
#jdbcrealm需要用到的数据源
jdbcrealm.dataSource=$dataSource
#开启查找权限
jdbcrealm.permissionsLookupEnabled=true
#设置安全管理器使用的jdbcrealm
securityManager.realms=$jdbcrealm
**shiro_jdbcrealm_mysql.ini (mysql+druid)**
#数据源
dataSource=com.alibaba.druid.pool.DruidDataSource
#数据源链接要素
dataSource.driverClassName=com.mysql.jdbc.Driver
#mysql 3306 sqlserver 1433
dataSource.url=jdbc:mysql://localhost:3306/szqy07
dataSource.username=root
dataSource.password=root
#jdbcrealm
jdbcrealm=org.apache.shiro.realm.jdbc.JdbcRealm
#jdbcrealm需要用到的数据源
jdbcrealm.dataSource=$dataSource
#开启查找权限
jdbcrealm.permissionsLookupEnabled=true
#设置安全管理器使用的jdbcrealm
securityManager.realms=$jdbcrealm
3.7.4,编写测试类ShiroTestC ShiroTestD
// 加载ini创建factory shiro_jdbcrealm_mysql.ini
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro_jdbcrealm_oracle.ini");
// 工厂类获取SecurityManager
SecurityManager securityManager = factory.getInstance();
//使他可以访问
SecurityUtils.setSecurityManager(securityManager);
//获取当前Subject
Subject currentUser = SecurityUtils.getSubject();
//判断是否认证
if(!currentUser.isAuthenticated())
//收集客户端提交的用户和密码
AuthenticationToken token =
new UsernamePasswordToken("scott","tiger");
//new UsernamePasswordToken("smith","tiger");
//提交收集的数据给SecurityManager ,做认证处理 this.securityManager.login(this, token);
//org.apache.shiro.authc.Authenticator authenticate方法,认证
try
currentUser.login(token);
System.out.println("认证成功");
//授权
//角色
System.out.println("scott用户是否具有role1角色:"+currentUser.hasRole("role1"));//true
System.out.println("scott用户是否具有role3角色:"+currentUser.hasRole("role3"));//false
//权限
System.out.println("scott用户是否具有dept:select权限:"+currentUser.isPermitted("dept:select"));//true
System.out.println("scott用户是否具有dept:select权限:"+currentUser.isPermitted("dept:alter"));//false
catch (AccountException e)
System.out.println("用户名错误");
catch (IncorrectCredentialsException e)
System.out.println("密码错误");
catch (AuthenticationException e)
e.printStackTrace();
//当前用户退出
currentUser.logout();
4,知识点总结
5,本章面试题
shiro 认证流程:
1)收集认证信息(用户和密码)UsernamePasswordToken,subject调用login方法将信息提交SecurityManager
- SecurityManager将认证信息交给org.apache.shiro.authc.Authenticator
3)realm 访问数据源,获取安全数据,并且做封装(AuthenticationInfo)
4) SecurityManager获取realm从数据库(或者其他数据源)拿到的安全数据
5)org.apache.shiro.authc.Authenticator调用Authenticate方法,进行验证
授权流程:
org.apache.shiro.authz.Authorizer
创作打卡挑战赛
赢取流量/现金/CSDN周边激励大奖
以上是关于JAVAEE安全框架之shiro第一课的主要内容,如果未能解决你的问题,请参考以下文章
杨老师课堂之JavaEE三大框架Hibernate入门第一课