JavaWEB开发框架:Shiro
通过了三个月的实习,在javaWEB开发的过程当中,学习到了一些新的知识,特此记录一下学习到的一种新的WEB开发当中常用的用户认证和授权的安全框架,Shiro。
首先,要先知道shiro这个框架主要在WEB开发当中的定位,其主要的定位是用于做用户登录和权限控制的一个安全框架,可以帮助我们更加安全的完成用户的登录以及授权的相关过程,并且其使用和配置都十分的简便,还可以集成到SpringMVC当中,并且还能配合各类数据库的使用,包括mysql、nosql等,所以可谓十分强大但又很简练的安全框架。
要开始学习shiro的使用,我们要先引入一些在shiro当中的一些核心功能的概念:
1.Authentication(身份验证):简称为“登录”,即验证当前登录的用户是否是我们项目当中的合法用户。
2.Authorization(授权):不同级别的合法用户的访问权限的控制过程,即决定哪些用户拥有哪些权限去访问某些的资源。
3.Session Management(会话管理):管理用户特定的会话,即使在非 Web 或 EJB 应用程序。
4.Cryptography(加密):通过使用加密算法保持数据安全
shiro由于其定位的因素,本文主要介绍其中的两个核心方法:认证(Authentication)和授权(Authorization)
在介绍功能之前,我们还要简单的介绍一下在shiro框架当中的主要使用的组件的概念:
1.Subject : 正与系统进行交互的对象,在web开发当中,可以理解成每一台想要访问web的用户,都是一个subject。
2.SecurityManager:顾名思义,就是用于安全事务的管理的对象,Shiro 架构的核心,其用来协调内部各安全组件,管理内部组件实例,并通过它来提供安全管理的各种服务。当 Shiro 与一个 Subject 进行交互时,实质上是幕后的 SecurityManager 处理所有繁重的 Subject 安全操作。
3.Realms :本质上是一个特定安全的 DAO,即一个安全的数据源。当配置 Shiro 时,必须指定至少一个 Realm 用来进行身份验证和/或授权。Shiro 提供了多种可用的 Realms 来获取安全相关的数据。如关系数据库(JDBC),INI 及属性文件等。可以定义自己 Realm 实现来代表自定义的数据源。
对以上这些概念有了一些理解之后,就基本掌握了shiro的大体概念了,那么接下来,我们来了解shiro如何使用,二话不说,先上我们的maven依赖文件:
<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> <groupId>bjtu.wellhold</groupId>
<artifactId>Shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>Shiro</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency> <dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency> </dependencies>
</project>
测试框架我们使用的是junit,shiro的版本我们使用的是1.2.3,这里要额外提一句,版本的不同,对一些功能的实现会有不兼容的现象(报错),所以一定要注意版本的问题。
在上头介绍概念的时候,有提到过Realms 的概念,它是一个要验证的数据源,可以把它理解成一个安全的用于存储程序合法用户信息——用户名、密码、以及该用户的权限的仓库,所有用户提交的账户信息都需要到这里头进行验证,看看用户提交的账户信息是否存在于Realms里头,一判定当前想要登录的用户是否是程序的合法用户,如果是合法用户,那么还可以获取到该用户的一个安全权限,即该用户可以访问什么内容,不可以访问什么内容,可能解释的有点繁琐,也不是很全面,但是这样的解释比较利于理解。在介绍Realms 概念的时候,有提到Realms 的数据源可以是JDBC,也可以是.ini文件,那么接下来我们就分别对两种形式进行介绍。
1.通过.ini作为信息数据源
在shiro1.2.3.版本之后,在我们没有指定相应的Realms的实现类的时候,shiro会自动的为我们生成一个iniRealm实例供程序使用,所以我们只需要指定我们的信息源.ini文件即可,下边是我们的.ini文件内容:
#自定义数据源 格式 用户名=密码 [users] Floder=Floder
内容很简单,只是指定了一个用户名为Floder,密码为Floder的用户,因为这里只涉及讲解身份认证,所以并没有对这个用户的权限内容进行编写。接下来看看我们的代码:
//认证过程
public void demo1()
{
//该demo中没有配置realm,是因为Shiro,默认提供了一个叫IniRealm的实例在进行的
//在1.2.3版本之后才有的 //设置securityManage环境
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager manager=factory.getInstance();
SecurityUtils.setSecurityManager(manager); //模拟subject
Subject subject=SecurityUtils.getSubject();
UsernamePasswordToken token =new UsernamePasswordToken("Floder","Floder");
try{
subject.login(token);
}catch(AuthenticationException e)
{
e.printStackTrace();
} boolean isOK=subject.isAuthenticated();
System.out.println("用户是否登录:"+isOK); //模拟用户退出登录
subject.logout();
isOK=subject.isAuthenticated();
System.out.println("用户是否登录:"+isOK);
}
代码当中,首先获取到一个SecurityManager的工厂实例,并且为这个实例指定了我们的数据源文件,之后通过工厂实例生成了一个SecurityManager的实例,再为SecurityUtils去配置SecurityManager,这样我们的程序就有一个SecurityManager用于管理所有安全行为的过程了,并对所有的安全行为负责(这里提到的安全行为主要指的是身份认证和授权过程)。之后我们通过模拟一个subject和token(令牌)去模拟用户登录的过程,通过执行subject.login,将令牌信息提交到subject之后,再执行subject.isAuthenticated(),对提交的令牌进行身份认证,即到我们配置的.ini文件当中去查找是否存在这个合法用户,如果存在则返回true,如果不存在则返回false,执行结果如下:
接下来我们再来看看的授权过程,这时候我们要在.ini文件当中配置到合法用户的相关权限:
#用户对应的角色 #用户Floder拥有角色role1和role2 [users] Floder = Floder,role1,role2 floder=floder,role2 #角色对应的资源权限 [roles] #权限标识符符号规则 资源:操作:实例 user:create:01 表示对用户资源实例01进行create操作 #user:create 表示对资源进行create操作,相当于user:create:* #user:*:01 表示对用户资源实例01进行所有操作 role1=user:create,user:update role2 = user:create
在.ini文件当中,我们配置了两个合法用户,并且我们制定了相应的roles,即角色,相信熟悉较为高级的数据库的人对这个概念不陌生,角色的不同,拥有的不同权限,在我们的demo中,角色1有创建用户和更新用户的权限,而角色2则只有创建用户的权限。接下来看我们的授权demo的代码:
@Test
//授权过程
public void demo2()
{
//该demo中没有配置realm,是因为Shiro,默认提供了一个叫IniRealm的实例在进行的
//在1.2.3版本之后才有的 //设置securityManage环境
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");
SecurityManager manager=factory.getInstance();
SecurityUtils.setSecurityManager(manager); Subject subject = SecurityUtils.getSubject();
// Subject subject2 = SecurityUtils.getSubject();
//提交认证是说携带的信息储存在 Token 中
UsernamePasswordToken token = new UsernamePasswordToken("Floder", "Floder");
// UsernamePasswordToken token2 = new UsernamePasswordToken("floder", "floder");
//模拟获取登陆
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
} //基于角色判断
//判断认证用户是否有role1角色
boolean isHasRole=subject.hasRole("role1");
System.out.println("判断当前用户是否有role1角色:"+isHasRole); //判断当前用户是否拥有多个角色
boolean isHasAllRoles=subject.hasAllRoles(Arrays.asList("role1","role2"));
System.out.println("判断当前用户是否有多个角色:"+isHasAllRoles); //基于资源判断
//判断当前用户是否能对该资源进行单一操作
boolean isPermitted=subject.isPermitted("user:create");
System.out.println("判断当前用户是否能对该资源进行单一操作:"+isPermitted); //判断已认证用户的资源是否拥有对应的多个操作
boolean isPermittedAll = subject.isPermittedAll("user:create","user:update");
System.out.println("判断已认证用户的资源是否拥有对应的多个操作 "+isPermittedAll);
}
在代码当中,当前合法用户进行了各种角色和授权的行为判别,即管理当前用户所具有的安全权限。运行结果如下:
将我们的token换成token2进行subject.login,再看运行结果:
符合我们在.ini文件当中指定的内容。到此,我们基于.ini配置文件的身份认证和授权管理过程就讲的差不多了,因为是使用的shiro提供的自动生成的realm,所以所需要编写的东西还是相对简单的,接下来我们来学习通过我们自定义的realm来做身份认证和授权的过程。
2.通过自定义Realms作为信息数据源
首先先看我们自定义的Realms实现类的代码:
package bjtu.wellhold.Shiro; import java.util.ArrayList;
import java.util.List; import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection; public class UserRealm extends AuthorizingRealm { @Override
public void setName(String name) {
super.setName("userName");
} //授权(依赖于认证信息)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String userId = (String) principals.getPrimaryPrincipal();
//模拟从数据库得来的角色信息
List<String> listPermission = new ArrayList<String>();
listPermission.add("user:create");
listPermission.add("user:update"); //将权限信息保存到AuthorizationInfo中
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for(String permission:listPermission){
simpleAuthorizationInfo.addStringPermission(permission);
}
return simpleAuthorizationInfo;
} //认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //得到token的用户信息
// 从token中取出身份信息, 即用户的username
String userId = (String) token.getPrincipal();
System.out.println("token传入的用户名:"+userId);
String password = new String((char[])token.getCredentials());
System.out.println("token传入的密码:"+password); //可以到数据库当中的某张表查询是否存在该合法用户信息 //模拟数据库返回数据,存在就返回一个credentials
String username1 = "Floder";//数据库当中存在的用户名
String password1 = "Floder";//数据库当中存在的密码
if(!username1.equals(userId)) throw new UnknownAccountException("没有这个用户!");
else if(!password1.equals(password)) throw new IncorrectCredentialsException("密码错误!");
else
{
//实际上在返回SimpleAuthenticationInfo的时候
//subject.login当中的token还是会和SimpleAuthenticationInfo当中的用户名密码进行匹配一次
//不过为了可控的抛出exception信息,我们还是在方法中就进行验证用户名和密码
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userId, password1, this.getName());
return info;
}
} }
在代码中,我们自定义了一个实现类叫UserRealm,它继承了父类AuthorizingRealm,继承只要,需要实现两个方法,分别是身份认证方法:doGetAuthenticationInfo,以及授权方法:doGetAuthorizationInfo。
我们首先来说身份认证方法的实现逻辑,我们先来看看我们的demo函数的写法,与使用shiro提供的自定义Realm当中的demo函数的逻辑相似:
//采用自定义realm
@Test
public void demo3()
{
//设置securityManage环境
IniSecurityManagerFactory factory=new IniSecurityManagerFactory("classpath:shiro-single-Aurealm.ini");
SecurityManager manager=factory.getInstance();
SecurityUtils.setSecurityManager(manager); //模拟subject
Subject subject=SecurityUtils.getSubject();
UsernamePasswordToken token =new UsernamePasswordToken("Floder","Floder");
try{
subject.login(token);
}catch(AuthenticationException e)
{
e.printStackTrace();
} boolean isOK=subject.isAuthenticated();
System.out.println("用户是否登录:"+isOK); //模拟用户退出登录
subject.logout();
isOK=subject.isAuthenticated();
System.out.println("用户是否登录:"+isOK);
}
demo函数当中,还是以往的套路,首先生成一个工厂的实例,在生成工厂实例的过程当中指定.ini配置文件,但是这一次,不在.ini内写用户信息,而是在.ini内写自定义的Realm信息:
userRealm=bjtu.wellhold.Shiro.UserRealm
securityManager.realms=$userRealm
在配置完SecurityManage环境之后,我们执行subject.login,这个时候,会自动调用到自定义的Realm类里头的doGetAuthenticationInfo方法,并且把token传过去,这时候我们就可以在doGetAuthenticationInfo方法当中做用户的身份认证,信息源可以是mysql等数据库,并且还可以对传入的token进行用户名密码的分别认证,然后如果不属于合法信息用户还可以分别跑出不同的异常信息进行提示,在代码当中的注释也写的很明白了,这里就不再进行赘述了。
看完自定义类的身份认证,我们再来看自定义类的授权过程,首先看授权的demo代码:
@Test
public void demo4() { IniSecurityManagerFactory factory=new IniSecurityManagerFactory("classpath:shiro-single-Aurealm.ini");
SecurityManager manager=factory.getInstance();
SecurityUtils.setSecurityManager(manager); //模拟subject
Subject subject=SecurityUtils.getSubject();
UsernamePasswordToken token =new UsernamePasswordToken("Floder","Floder");
try{
subject.login(token);
}catch(AuthenticationException e)
{
e.printStackTrace();
} //基于资源判断
//判断当前用户是否能对该资源进行单一操作
boolean isPermitted=subject.isPermitted("user:create");
System.out.println("判断当前用户是否能对该资源进行单一操作:"+isPermitted); //判断已认证用户的资源是否拥有对应的多个操作
boolean isPermittedAll = subject.isPermittedAll("user:create","user:update");
System.out.println("判断已认证用户的资源是否拥有对应的多个操作 "+isPermittedAll); }
对传入的token进行了身份认证之后,在进行授权确认,在doGetAuthorizationInfo的方法当中,对token当中的信息,进行了授权信息的查询(可以从配置文件当中查询,也可以从数据库当中查询)查询之后通过simpleAuthorizationInfo实例对象返回到demo函数当中,具体的可以通过代码当中的注释很明了的看出,这里就不进行赘述了。
到此为止,基于.ini文件配置以及基于自定义的Realm的方式使用Shiro进行用户登录的身份认证和授权的demo就讲解完毕了,可能总结的比较片面,描述的也比较简单,如有描述不正确的地方,还请联系本人,相互交流一下。
JavaWEB开发框架:Shiro的更多相关文章
- JavaWeb项目:Shiro实现简单的权限控制(整合SSM)
该demo整合Shiro的相关配置参考开涛的博客 数据库表格相关设计 表格设计得比较简单,导航栏直接由角色表auth_role的角色描述vRoleDesc(父结点)和角色相关权限中的权限描述(标记为 ...
- 转:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法、shiro认证与shiro授权
原文地址:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法.shiro认证与shiro授权 以下是部分内容,具体见原文. shiro介绍 什么是shiro shiro是Apache ...
- 【JavaWeb】CVE-2016-4437 Shiro反序列化漏洞分析及代码审计
Shiro反序列化漏洞分析及代码审计 漏洞简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理. Apache Shiro默认使用了CookieRe ...
- [JavaWeb]Shiro漏洞集合——代码审计
Shiro漏洞集合 Shiro其实就是一组Filter,他会进行验证,鉴权,会话 Management,再把请求转到web过滤器.所以最好先去对Shiro有个整体性的了解. 复现环境:https:// ...
- JavaWeb、J2-SE开发框架——Spring
相关博客: 2.spring官网 1.使用Spring的jdbcTemplate进一步简化JDBC操作
- shiro退出登陆清空缓存实现
上一篇介绍了使用springmvc集成shiro登陆过程(http://www.cnblogs.com/nosqlcoco/p/5579081.html),通过FormAuthenticationFi ...
- JavaWeb应用开发架构浅谈
本文就我所经历和使用过的技术和框架, 讨论 Java / Javascript 技术组合构成的Web 应用架构. 一. 概述 Web 应用架构可以划分为两大子系统:前端子系统和后台子系统. 前端子系统 ...
- javaweb学习总结(二十一)——JavaWeb的两种开发模式
SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式. 一.JSP+JavaBean开发模式 1 ...
- javaweb的开发模式
SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式. 一.JSP+JavaBean开发模式 1 ...
随机推荐
- struts2的验证
1.原理 当浏览器向服务器提交表单数据时,在服务器端需要对表单数据的有效性进行校验. “校验方法”会在“业务方法”之前调用. 2.实现验证的两种方式 struts2校验的两种实现方法: 1. 手工编写 ...
- [bzoj4071] [Apio2015]巴邻旁之桥
Description 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B. 每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 10000 ...
- [Leetcode] Longest consecutive sequence 最长连续序列
Given an unsorted array of integers, find the length of the longest consecutive elements sequence. F ...
- CF869E The Untended Antiquity 解题报告
CF869E The Untended Antiquity 题目描述 \(\text{Adieu l'ami}\). Koyomi is helping Oshino, an acquaintance ...
- 【BZOJ 4034】[HAOI2015]树上操作 差分+dfs序+树状数组
我们只要看出来这道题 数组表示的含义就是 某个点到根节点路径权值和就行 那么我们可以把最终答案 看做 k*x+b x就是其深度 ,我们发现dfs序之后,修改一个点是差分一个区间,修改一个点的子树,可以 ...
- 自定义CheckBox
自定义android的CheckBox按钮图形有两个步骤三种方式: 第一步: 新建Android XML文件,类型选Drawable,根结点选selector,放置在drawable文件夹内,指定各种 ...
- 实际上ECMAScript中并没有对类的定义
首先,我们用一个经典例子来简单阐述一下ECMAScript中的继承机制. 在几何学上,实质上几何形状只有两种,即椭圆形(是圆形的)和多边形(具有一定数量的边).圆是椭圆的一种,它只有一个焦点.三角形. ...
- Membership Service Providers (MSP)
https://blog.csdn.net/baidu_39649815/article/details/76468249 Membership service provider (MSP)是一个提供 ...
- rman备份与异机恢复
一.rman备份脚本并为定时任务 #!/bin/bashsource ~/.bash_profileexport LANG=en_USBACKUP_DATE=`date +%d`#RMAN_LOG_F ...
- 解析 Array.prototype.slice.call(arguments,0)
Array.prototype.slice.call(arguments,0) 经常会看到这段代码用来处理函数的参数 网上很多复制粘帖说:Array.prototype.slice.call(argu ...