Shiro框架01

馋奶兔 提交于 2020-02-25 02:02:29

Shiro框架01

1.Shiro简介

[1]为什么要学习Shiro

A、目前遇到的问题:
使用RABC进行角色访问控制的时候,代码书写起来比较麻烦。
目前学习的写的操作代码整体不太安全。

B、解决方案:
Spring securing :可以解决以上问题
缺点: 基于Spring之上的,局限性比较大
Shiro :可以用在JavaEE、JavaSE和分布式项目中。
什么是Shiro
Apache Shiro 是一个强大而灵活的开源安全框架,它干净利落地处理身份认证授权企业会话管理加密
Shior官网:http://shiro.apache.org/

[2]Shiro中的体系组成

在这里插入图片描述
A、Authentication:身份验证,就是我们平时所做的登录。
B、Authorization:授权,赋予角色不同的菜单、功能。
C、Session Management:管理登录用户的信息。
D、Cryptography:加密技术。MD5加密算法等。

a、Web Support:Shiro 对 web项目的支持
b、Caching:缓存 可以安全快速的操作
c、Concurrency:Apache Shiro 利用它的并发特性来支持多线程应用程序。
d、Testing:测试。
e、Run As:可以实现一个用户被允许的情况下,使用另一个用户访问
f、Remember Me:记住我

[3]Shiro整体架构

在这里插入图片描述
Subject:(org.apache.shiro.subject.Subject)
当前与软件进行交互的实体(用户,第三方服务程序,cron job,等等)的安全特定“视图”。
SecurityManager:SecurityManager 是 Shiro 架构的心脏。它基本上是一个“保护伞”对象,协调其管理的组件以确保它们能够一起顺利的工作。
Realms:域
Realms 在 Shiro 和你的应用程序的安全数据之间担当“桥梁”或“连接器”。当它实际上与安全相关的数据如用来执行身份验证(登录)及授权(访问控制)的用户帐户交互时,Shiro 从一个或多个为应用程序配置的 Realm 中寻找许多这样的东西。

2.Shiro环境的搭建

A、导包
在这里插入图片描述
B、书写shiro.ini文件(IDEA需要下载Ini插件----关闭防火墙)

[users]
zs=123
sxt=root

C、书写测试代码

package cn.qt.shiro1;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
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;

public class TestA {
    public static void main(String[] args) {
        //[1]解析Shiro.ini文件
        Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
        //[2]通过SecurityManager工厂获得SecurityManager实例
        SecurityManager securityManager = factory.getInstance();
        //[3]把SecurityManager对象设置到运行环境中
        SecurityUtils.setSecurityManager(securityManager);
        //[4]通过SecurityUtils获得Subject
        Subject subject = SecurityUtils.getSubject();
        //[5]书写自己的账户和密码----相当于用户自己输入的账户和密码
        //我们拿自己书写的账户和密码去和shiro.ini中的账户密码比较
        UsernamePasswordToken token = new UsernamePasswordToken("zs","1223");
        //[6]进行身份的验证
        try {
            subject.login(token);
            //[7]通过方法判断是否登录成功
            if (subject.isAuthenticated()){
                System.out.println("登录成功");
            }
        } catch (UnknownAccountException e) {//用户名错误
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){//密码错误
            System.out.println("密码错误");
        }
    }
}

3.Shiro中的异常分析

A、账户失效异常

DisabledAccountException

B、竞争次数过多-----账户和密码在多地同时登录

ConcurrentAccessException

C、尝试次数过多

ExcessiveAttemptsException

D、用户名不正确

UnknownAccountException

E、凭证不正确----密码不正确

IncorrectCredentialsException

F、凭证过期

ExpiredCredentialsException

使用Shiro做异常处理的时候,尽量把异常信息表示的婉转一点,比如用户名错误或者是密码错误,给出的提示信息均为“用户名或密码错误”,而不是单独把“用户名错误”或“密码错误”提示给用户。这样做有助于提升代码的安全性(防止用户恶意尝试登陆)。

4.Shiro的认证流程

A、通过 shiro 相关的 API 创建了 SecurityManager 以及获得
subject 实例

B、封装了 token 信息

C、详细描述
通过 subject.login(token)进行用户认证
Subject 接受 token 信息 ,通过 DelegatingSubject
将 token 委托给 securityManager 完成认证
securityManager 通过使用 DefaultSecurityManager
完成相关功能由 DefaultSecurityManager 中的 login 方法完
成对应的认证在 login 中调用了
AuthenticatingSecurityManager
中的 authenticate 方法完成认证
使用其中的doAuthenticate 获得realms信息如果是单个直接进
行比较,判断是否成功,如果是多个 raalm 需要使用验证策略完成
对应的认证工作

5.Shiro中的JdbcRealm

jdbcRealm中封装好了查询语句,使用JDBC进行数据的查询操作。但是由于数据源不确定,即不知道开发者用的是mysql、oracle还是其他的数据库。因此在jdbcRealm中并没有dataSource赋值,需要在ini文件中配置数据源,通过扫描解析ini文件中的内容给dataSource赋值。
作用:用于连接数据源(数据库)
在这里插入图片描述
A、mysql中的用户表如下所示
在这里插入图片描述
B、这里我们用c3p0连接数据源,ini文件如下所示

[main]
#获得数据源
dataSou=com.mchange.v2.c3p0.ComboPooledDataSource
dataSou.driverClass=com.mysql.jdbc.Driver
dataSou.jdbcUrl=jdbc:mysql://localhost:3306/shiro
dataSou.user=root
dataSou.password=1111
#配置jdbcRealm
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSou

#设置securityManager中的realm
securityManager.realm=$jdbcRealm

C、测试代码如下所示

package cn.qt.shiro1;

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.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

public class TestB {
    public static void main(String[] args) {

        //[1]解析Shiro.ini文件
        Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro-jdbc.ini");
        //[2]通过SecurityManager工厂获得SecurityManager实例
        SecurityManager securityManager = factory.getInstance();
        //[3]把SecurityManager对象设置到运行环境中
        SecurityUtils.setSecurityManager(securityManager);
        //[4]通过SecurityUtils获得Subject
        Subject subject = SecurityUtils.getSubject();
        //[5]书写自己的账户和密码----相当于用户自己输入的账户和密码
        //我们拿自己书写的账户和密码去和shiro.ini中的账户密码比较
        UsernamePasswordToken token = new UsernamePasswordToken("zs","123");
        //[6]进行身份的验证
        try {
            subject.login(token);
            //[7]通过方法判断是否登录成功
            if (subject.isAuthenticated()){
                System.out.println("登录成功");
            }
        } catch (UnknownAccountException e) {//用户名错误
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){//密码错误
            System.out.println("密码错误");
        }
    }
}

在实际的项目中数据库表名和数据库字段名一般不会和JdbcRealm类中封装的语句保持一致,这就需要我们自定义realm.

6.认证策略

规定了如果有多个数据源时应该如何操作
A、AtLeastOneSuccessfulStrategy:
如果一个(或更多)Realm 验证成功,则整体的尝试被认为是成功的。如果没有一个验证成功,则整体尝试失败。类似于java中的 |
如果ini文件中培养配置认证策略,则默认的认证策略为该策略

B、FirstSuccessfulStrategy
只有第一个成功地验证的 Realm 返回的信息将被使用。所有进一步的 Realm 将被忽略。如果没有一个验证成功,则整体尝试失败。 类似于java中的||

C、AllSucessfulStrategy:
为了整体的尝试成功,所有配置的 Realm 必须验证成功。如果没有一个验证成功,则整体尝试失败。
在ini文件中中配置策略
1.数据源
数据源1---->shiro数据库中的users表
在这里插入图片描述
数据源2—>shiro2数据库中的users表
在这里插入图片描述
分析可知:两个数据源中有相同的数据zs—123,有不同的数据:数据源1中lisi—123,而数据源2中则是ww—123

ini文件如下

[main]
#获得数据源1
dataSou=com.mchange.v2.c3p0.ComboPooledDataSource
dataSou.driverClass=com.mysql.jdbc.Driver
dataSou.jdbcUrl=jdbc:mysql://localhost:3306/shiro
dataSou.user=root
dataSou.password=1111
#配置jdbcRealm1
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSou


#获得数据源2
dataSou2=com.mchange.v2.c3p0.ComboPooledDataSource
dataSou2.driverClass=com.mysql.jdbc.Driver
dataSou2.jdbcUrl=jdbc:mysql://localhost:3306/shiro2
dataSou2.user=root
dataSou2.password=1111
#配置jdbcRealm2
jdbcRealm2=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm2.dataSource=$dataSou2

#配置验证器
authenticationStrategy= org.apache.shiro.authc.pam.FirstSuccessfulStrategy

#设置securityManager中的realm
securityManager.realms=$jdbcRealm,$jdbcRealm2
securityManager.authenticator.authenticationStrategy=$authenticationStrategy

测试文件TestA

package cn.qt.shiro2;

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.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

public class TestA {
    public static void main(String[] args) {

        //[1]解析Shiro.ini文件
        Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro-jdbc.ini");
        //[2]通过SecurityManager工厂获得SecurityManager实例
        SecurityManager securityManager = factory.getInstance();
        //[3]把SecurityManager对象设置到运行环境中
        SecurityUtils.setSecurityManager(securityManager);
        //[4]通过SecurityUtils获得Subject
        Subject subject = SecurityUtils.getSubject();
        //[5]书写自己的账户和密码----相当于用户自己输入的账户和密码
        //我们拿自己书写的账户和密码去和shiro.ini中的账户密码比较
        UsernamePasswordToken token = new UsernamePasswordToken("ww","123");
        //[6]进行身份的验证
        try {
            subject.login(token);
            //[7]通过方法判断是否登录成功
            if (subject.isAuthenticated()){
                System.out.println("登录成功");
            }
        } catch (UnknownAccountException e) {//用户名错误
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){//密码错误
            System.out.println("密码错误");
        }
    }
}

测试结果:
A、验证器:AtLeastOneSuccessfulStrategy或FirstSuccessfulStrategy
登录时:无论是登录zs、lisi还是ww,结果均为登录成功。
结论:AtLeastOneSuccessfulStrategy和FirstSuccessfulStrategy两种验证方式结果相同,即多个数据源中,只要有一个数据源返回登录成功,结果就为成功。两者的区别是:AtLeastOneSuccessfulStrategy相当于java中的 |,所有项都会执行,FirstSuccessfulStrategy则相当于java中的 || ,只要有一个为true,则直接返回true,后续的项不再判断。
B、验证器AllSucessfulStrategy
登录时:登录zs,则登录成功,lisi或ww则显示用户名错误
结论:必须所有的数据源都含有zs这条数据才返回true

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!