Shiro

发布于:2023-01-19 ⋅ 阅读:(166) ⋅ 点赞:(0)

一、权限控制

        1、什么是权限管理

        2、用户的认证

        3、用户的授权

        4、权限控制的实现方案

        5、权限控制的数据库设计

二、Shiro基本概念

Shiro官网:http://shiro.apache.org/
中文帮助文档:http://greycode.github.io/shiro/doc/reference.html

2.1 Shiro 介绍

Apache Shiro (发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大灵活的开源安全框架,提供认证、授权、会话管理以及密码加密等功能,由Java语言开发。

使用 Apache Shiro 可以做到:

● 验证用户身份。

● 对用户进行访问控制,比如:

● 判断某个用户是否被赋予某个特定角色

● 判断某个用户是否被允许执行某些操作

● 可以在各种环境下使用 Session API ,即使是不在web或EJB容器中。

● 对认证、访问控制或在会话生命周期中的事件进行响应处理。

● 可以聚合使用一个或多个安全数据的数据源而使用者只需了解一层抽象 。

● 使用单点登录(SSO)。

● 使用“下次自动登陆(Remember Me)”。

►Shiro 力图在各种应用环境中做到以上——从最简单的命令行程序到庞大的企业应用——而且不依赖于任何的第三方框架、容器或者应用服务器,当然它可以集成到这些环境中,但也能独立使用。

2.2 Shiro功能

Apache Shiro 框架提供了很多功能,下图展示了Shiro的着重点

(1)Shiro主要面向Shiro开发团队所谓的“应用安全的四大基础” ——认证,授权,会话管理与密码加密

认证 或“登录”,用以验证用户身份。

授权访问控制, 比如决定谁可以访问某些资源。

会话管理 管理用户相关的session,即使是在非web或EJB应用中。

加密可以非常方便地使用(各种)加密算法保证数据的安全。

(2)Shiro还包含了一些其他功能以支持不同的应用环境,其中:

对Web的支持 Shiro自带的支持Web的API可以很容易地保证web应用的安全。

缓存缓存在Apache Shiro的API中是“一等公民”,可以保证操作的快速高效。

并发 Apache Shiro的并发功能支持开发多线程的应用。

测试对测试的支持可以帮助你编写单元测试与集成测试。

“自动登陆”(Remember Me)可以跨会话记住用户身份,只在某些特殊情况下才需要强制登录。

2.3 Shiro中的几个重要概念

Shiro的架构主要有三个顶级概念:Subject, SecurityManager和Realms

Subject正如我们在教程里提到的, Subject实际上是安全领域里的“当前执行用户”。“User”通常意指人类,而Subject 却可以是一个人,或者是第三方服务,守护进程帐户,定时任务等等——可以是基本上任何正与软件交互的事物。Subject的实例都会(也是必须)绑定一个SecurityManager,对Subject的操作会转为Subject与SecurityManager之间的交互。

SecurityManager SecurityManager是Shiro架构的核心,像个“保护伞”一样协调内部组件运作。不过一旦SecurityManager配置完成,它就被放一边去了,通常开发人员只需要使用Subject。

Realms Realms在Shiro和你的安全数据之间扮演“桥梁”或“连接器”的角色。当需要用到安全数据比如用户帐号来进行认证或授权时,Shiro会从应用配置的一个或多个Realms中来查找。这个意义上Realm其实就是安全(操作)特有的DAO:它封装了数据源的细节,并在Shiro需要时提供相关数据。配置Shiro时,必须至少得有一个Realm以用于认证(和/或)授权。在SecurityManager中可以配置Realm,至少得有一个。Shiro自带一些开箱即用的Realms用于连接像是LDAP,关系数据库(JDBC),文本配置源(比如INI)以及属性文件等等这样的安全数据来源。如果默认的不能满足你的需要,你也可以自己实现一个Realm。

下面是Shiro的核心架构

Subject

安全视角下与软件交互的实体(用户,第三方服务等等)

SecurityManager

如上,SecurityManager 是Shiro架构的核心,起协调内部各组件的作用。同样也管理Shiro的所有用户,可以进行用户相关的安全操作

Authenticator

Authenticator是用于执行用户的认证行为的组件,当用户登录时,登录的逻辑操作由Authenticator执行。Authenticator知道如何与存储用户/帐号信息的一个或多个Realms协作。从这些Realms获取的数据用于验证用户的真实身份

Authorizer

Authorizer用于控制程序中用户的访问,最终决定用户是否被允许做什么。和Authenticator一样, Authorizer也会与底层的数据源交互来获取角色与权限信息,从而准确控制用户的行为。

SessionManager

SessionManager可以创建用户会话(Session)维护其生命周期并在所有环境中提供了强大的用户体验。Shiro可以管理任何环境下的用户会话,即使是非Web/Servlet或EJB容器下——这是区别于其他安全框架而特有的功能。默认情况下,Shiro会使用(容器)提供的会话管理机制,但如果没有,比如在独立的程序或非Web环境中,Shiro将使用它内置的企业会话管理功能以提供一致的编程体验。SessionDAO可使用各种数据源来存储会话。

SessionDAO

SessionDAO替SessionManager执行会话的存储操作(CRUD)。任何的数据存储都可以接入到会话管理中

CacheManager

CacheManager创建与管理缓存的实例与生命周期以供Shiro中的其他组件使用。因为在Shiro中可以使用多个数据源用于认证、授权和会话管理,而缓存可以在使用这些数据源时改善性能,所以也就成为框架中很重要的一个功能。 Shiro中可以使用任何开源或商业的缓存方案。

Cryptography

加密是企业级安全框架固有的功能,Shiro的crypto包对密码、哈希和各种编码实现提供了易于使用和理解的封装 。该包下的所有类都为此精心设计,那些用过Java的原生安全加密功能的人应该了解这是多么大的一个挑战。Shiro的加密API简化了Java加密中的复杂性

Realms

Realm在shiro与应用安全数据之间扮演“桥梁”/“连接器”的角色。每当因为执行认证或授权操作而需要像用户帐号等安全相关的数据时,shiro会从程序配置的一个或多个Realm中查询。你可以根据需要配置多个Realm

三、Shiro认证

身份验证:即在应用中谁能证明他就是他本人。一般提供如他们的身份 ID 一些标识信息来表明他就是他本人,如提供身份证,用户名 / 密码来证明。

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

principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个 Primary principals,一般是用户名 / 密码 / 手机号。

credentials:证明 / 凭证,即只有主体知道的安全值,如密码 / 数字证书等。

最常见的 principals 和 credentials 组合就是用户名 / 密码了。接下来先进行一个基本的身份认证。

另外两个相关的概念是之前提到的 SubjectRealm,分别是主体及验证主体的数据源。

3.1 环境搭建

Pom.xml

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.1.3</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.1</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.9</version>
    <scope>provided</scope>
</dependency>

3.2 默认Relam实现认证

此处使用 ini 配置文件,通过 [users] 指定了两个主体

配置文件:shiro-authenticate.ini

[users]
zhangsan=123
lisi=lisi

测试代码

//创建SecurityManager工厂,通过ini配置文件创建 SecurityManager工厂
Factory<SecurityManager> factory =
        new IniSecurityManagerFactory("classpath:shiro-authenticate.ini");

//创建SecurityManager
SecurityManager securityManager = factory.getInstance();
// 设置SecurityManager到运行环境中,保持单例模式
SecurityUtils.setSecurityManager(securityManager);

//获取subject
Subject subject = SecurityUtils.getSubject();

//用户名和密码token
UsernamePasswordToken usernamePasswordToken =
        new UsernamePasswordToken
                ("zhangsan","123");
//测试登录
subject.login(usernamePasswordToken);
//是否登录成功
boolean isOk = subject.isAuthenticated();
System.out.println(isOk);

//登出功能
subject.logout();
boolean loginOk = subject.isAuthenticated();
System.out.println(loginOk);

3.3 认证流程 

1、首先调用 Subject.login(token) 进行登录,其会自动委托给 Security Manager,调用之前必须通过 SecurityUtils.setSecurityManager() 设置;

2、SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;

3、Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;

4、Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;

5、Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证成功了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。

3.4 自定义Realm实现认证

自定义Reaml,需要实现或者继承框架中的realm类

此处既可以继承AuthenticatingRealm,也可以继承AuthorizingRealm,此处我们继承AuthenticatingRealm,专门用于处理认证的Reaml

public class MyRealm extends AuthenticatingRealm {
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String principals = (String) authenticationToken.getPrincipal();
        //此步操作应该是根据身份查询数据库密码
        String credential = "123";
        SimpleAuthenticationInfo simpleAuthenticationInfo =
                new SimpleAuthenticationInfo
                        (principals,credential,"myRealm");
        return simpleAuthenticationInfo;
    }
}

 配置文件:shiro-realm.ini

#声明一个realm
myRealm= com.suke.shiro.MyRealm
#指定securityManager的realms实现
securityManager.realms=$myRealm

测试代码

//创建SecurityManager工厂,通过ini配置文件创建 SecurityManager工厂
Factory<SecurityManager> factory =
        new IniSecurityManagerFactory("classpath:shiro-realm.ini");
SecurityManager securityManager = factory.getInstance();
// 设置SecurityManager到运行环境中,保持单例模式
SecurityUtils.setSecurityManager(securityManager);

//获取subject
Subject subject = SecurityUtils.getSubject();

//用户名和密码token
UsernamePasswordToken usernamePasswordToken =
        new UsernamePasswordToken
                ("admin","123");
//测试登录
subject.login(usernamePasswordToken);
//是否登录成功
boolean isOk = subject.isAuthenticated();

System.out.println(isOk);

3.5 自定义Reaml实现散列算法

散列算法

散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如 MD5、SHA 等。一般进行散列时最好提供一个 salt(盐),比如加密密码 “admin”,产生的散列值是 21232f297a57a5a743894a0e4a801fc3”,可以到一些 md5 解密网站很容易的通过散列值得到密码 “admin”,即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如用户名和 ID(即盐);这样散列的对象是 “密码 + 用户名 +ID”,这样生成的散列值相对来说更难破解。

String str = "hello";
String salt = "123";
String md5 = new Md5Hash(str, salt).toString()

Shiro 还提供了通用的散列支持

String str = "hello";
String salt = "123";
//内部使用MessageDigest
String simpleHash = new SimpleHash("SHA-1", str, salt).toString(); 

自定义支持散列算法的Reaml类

public class Md5Reaml extends AuthenticatingRealm {

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        String principals = (String) authenticationToken.getPrincipal();
        //此步操作应该是根据身份查询数据库密码
        String credential = "8a1c32303c0b5bd9e40ea15717f5f623";
        String salt = "qwar";
        SimpleAuthenticationInfo simpleAuthenticationInfo =
                new SimpleAuthenticationInfo
                        (principals,credential, ByteSource.Util.bytes(salt),"md5Realm");
        return simpleAuthenticationInfo;
    }
}

配置文件shiro-md5.ini

#凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1
#声明一个realm
md5Realm= com.suke.shiro.Md5Reaml
#将散列设置到当前reaml中
md5Realm.credentialsMatcher=$credentialsMatcher
#指定securityManager的realms实现
securityManager.realms=$md5Realm

测试类

//创建SecurityManager工厂,通过ini配置文件创建 SecurityManager工厂
Factory<SecurityManager> factory =
        new IniSecurityManagerFactory("classpath:shiro-md5.ini");
SecurityManager securityManager = factory.getInstance();
// 设置SecurityManager到运行环境中,保持单例模式
SecurityUtils.setSecurityManager(securityManager);

//获取subject
Subject subject = SecurityUtils.getSubject();

//用户名和密码token
UsernamePasswordToken usernamePasswordToken =
        new UsernamePasswordToken
                ("admin","111");
//测试登录
subject.login(usernamePasswordToken);
//是否登录成功
boolean isOk = subject.isAuthenticated();

System.out.println(isOk);

四、Shiro授权

4.1 授权概念

授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)

主体 主体,即访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。

资源 在应用中用户可以访问的URL,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

权限 安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如: 访问用户列表页面 查看/新增/修改/删除用户数据(即很多时候都是 CRUD(增查改删)式权限控制)打印文档等。如上可以看出,权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许,不反映谁去执行这个操作。所以后续还需要把权限赋予给用户,即定义哪个用户允许在某个资源上做什么操作(权限),Shiro 不会去做这件事情,而是由实现人员提供。Shiro 支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的)粗粒度:小红以部门经理登陆到系统中,能操作用户模块的所有资源

细粒度:小红以部门经理登陆到系统,但是只能操作本部门的所有资源

实例级别:可以操作用户模块中的使用户,具体操作哪一个用户的资源叫做实例资源

4.2 授权方式

Shiro 支持三种方式的授权:

编程式:通过写 if/else 授权代码块完成:


网站公告

今日签到

点亮在社区的每一天
去签到