安全认证框架之Shiro详解

  • 1. RBAC权限设计模型
    • 1.1. RBAC模型关键元素
      • 1.1.1. 权限
      • 1.1.2. 用户
      • 1.1.3. 角色
      • 1.1.4. 组
    • 1.2. 关键元素关系图
    • 1.3. 设计权限表结构
      • 1.3.1. RBAC权限设计(传统5张表)
      • 1.3.2. RBAC权限设计(11张表)
  • 2. Shiro简介
    • 2.1. 什么是Shiro
    • 2.2. 为什么要学Shiro框架
    • 2.3. 功能简介
    • 2.4. Shiro架构(从Shiro外部来看)
    • 2.5. Shiro架构(Shiro内部来看)
  • 3.Hello World
    • 3.1. 导入依赖包
    • 3.2. 创建log4j.properties及shiro.ini文件
    • 3.3. 创建Shiro测试类
    • 3.4 下一篇:[安全认证框架Shiro之认证](http://t.csdn.cn/YOb9l)

1. RBAC权限设计模型

        基于角色的权限访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注。在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。
        在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收。角色与角色的关系可以建立起来以囊括更广泛的客观情况。
        总结:通过给用户授予角色来获得权限。

1.1. RBAC模型关键元素

1.1.1. 权限

        系统的所有权限信息。权限具有上下级关系,是一个树状的结构。下面来看一个例子:
安全认证框架之Shiro详解_第1张图片
        对于上面的每个权限,又存在两种情况:一个是只是可访问,另一种是可授权。
例如:对于“查看用户”这个权限,如果用户只被授予“可访问”,那么他就不能将他所具有的这个权限分配给其他人。

1.1.2. 用户

        用户是应用系统的具体操作者,用户可以自己拥有权限信息,可以归属于0~N个角色,可属于0~N个组。他的权限集是自身具有的权限、所属的各角色具有的权限、所属的各组具有的权限的合集。它与权限、角色、组之间的关系都是n对n的关系。

1.1.3. 角色

        为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念。例如:系统管理员、管理员、用户、访客等角色。
        角色具有上下级关系,可以形成树状视图,父级角色的权限是自身及它的所有子角色的权限的综合。父级角色的用户、父级角色的组同理可推。

1.1.4. 组

        为了更好地管理用户,对用户进行分组归类,简称为用户分组。
        组也具有上下级关系,可以形成树状视图。
        在实际情况中,我们知道,组也可以具有自己的角色信息、权限信息。这让我想到我们的QQ用户群,一个群可以有多个用户,一个用户也可以加入多个群。每个群具有自己的权限信息。例如查看群共享。QQ群也可以具有自己的角色信息,例如普通群、高级群等。

1.2. 关键元素关系图

安全认证框架之Shiro详解_第2张图片

        权限、角色、组、用户之间都存在n对n的关系。且两两之间都有关系。

1.3. 设计权限表结构

安全认证框架之Shiro详解_第3张图片
        使用PowerDesigner工具进行数据库表模型的创建。

1.3.1. RBAC权限设计(传统5张表)

        创建Physical Data模型。
        (1)用户表

CREATE TABLE `t_user`(
	`uid` int primary key auto_increment,
	`uname` varchar(10),
	`password` varchar(20)
);

        (2)角色表

CREATE TABLE `t_role`(
	`rid` int primary key auto_increment,
	`rname` varchar(50),
	`permission` varchar(50)  #角色权限标记,给shiro使用的
);

        (3)权限表

CREATE TABLE `permission`(
	`pid` int primary key auto_increment,
	`pname` varchar(50),   # 权限名
	`permission` varchar(50)   # 权限
);

        (4)用户角色关系表

CREATE TABLE `t_user_role`(
	`uid` int refereness `t_user`(`uid`),
	`rid` int refereness `t_role`(`rid`)
);

        (5)角色权限关系表

CREATE TABLE `t_role_permission`(
`rid` int refereness `t_role`(`rid`),
`pid` int referenecss `t_permission`(`pid`)
);

1.3.2. RBAC权限设计(11张表)

安全认证框架之Shiro详解_第4张图片
安全认证框架之Shiro详解_第5张图片
安全认证框架之Shiro详解_第6张图片
安全认证框架之Shiro详解_第7张图片
安全认证框架之Shiro详解_第8张图片
安全认证框架之Shiro详解_第9张图片
安全认证框架之Shiro详解_第10张图片
安全认证框架之Shiro详解_第11张图片
安全认证框架之Shiro详解_第12张图片
安全认证框架之Shiro详解_第13张图片
安全认证框架之Shiro详解_第14张图片
(1)用户:如系统的使用账户(登录的用户)
        在 Shiro 中,代表访问系统的用户,被封装成 安全主体Subject对象;
(2)角色:拥有相同权限的用户
        是权限的集合,一种角色可以被很多个用户拥有,一种角色可以包含多种权限;【多对多】
(3)权限:系统中可以被用户操作的元素(如:系统菜单,超链接,文件,按钮等非常具体的)
        即操作资源的权利,比如访问某个页面,以及对某个模块的数据的CRUD操作权限。

2. Shiro简介

2.1. 什么是Shiro

        官网:http://shiro.apache.org/
        安全认证框架之Shiro详解_第15张图片
        Apache Shiro是一个Java安全(权限)框架。
        Shiro可以非常容易的开发出足够好的应用,不仅可用在JavaSE环境,也可以用在JavaEE环境。
        Shiro可以完成:认证、授权、加密、会话管理、与web集成、缓存等。
        下载地址:http://shiro.apache.org/download.html
        安全认证框架之Shiro详解_第16张图片
        git下载地址:https://github.com/apache/shiro

2.2. 为什么要学Shiro框架

  • Shrio是一个非常快速的完成认证、授权等功能,可以帮助我们完成认证、授权、加密、会话管理、与WEB集成、缓存等,且低成本开发的一套轻量级框架。
  • Shiro使用广泛,可在B/S(java项目)、C/S(web项目)、集群、分布式都可以使用。
  • Shiro与同类型框架相比:
    Spring security(前身Acegi security),非常依赖Spring,没有Shiro简单灵活。且shiro更加简单和易用,不需要研读源码可以根据api完成对ssm或spring boot的集成,而spring security做认证和授权时需要查看源码的工作流程。

2.3. 功能简介

        基本功能点如下图所示:
        安全认证框架之Shiro详解_第17张图片
        Authentication:认证器,身份认证/登录,验证用户(Subject)是不是拥有相应的身份;
        Authorization:授权器,即权限验证,是一个接口。验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限。
        Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,他的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的。另外,它可以用在分布式会话中,实现CAS单点登录。
        Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储。
        Web support:Web支持,可以非常容易的集成到Web环境的。
        Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率。
        Concurrency:Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去。
        Testing:提供测试支持。
        Run As:允许一个用户假装为另一个用户的身份证进行访问(如果另一个用户允许的情况下)。
        Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

2.4. Shiro架构(从Shiro外部来看)

        从外部来看Shiro,即从应用程序角度来观察如何使用Shiro完成工作:
        安全认证框架之Shiro详解_第18张图片
        Subject:应用代码直接交互的对象(即认证主体)。 也就是说Shiro的对外API核心就是Subject。Subject代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫、机器人等。与Subject的所有交互都会委托给SecurityManager。
        Subject其实是一个门面,SecurityManager才是实际的执行者。

        Subject包含 Principals 和 Credentials 两个信息:
                Principals:代表身份。可以是用户名、邮件、手机号码等等,用来标识一个登录主体的身份。
                Credentials:代表凭证。常见的有密码,数字证书等等。

        SecurityManager:安全管理器,即所有与安全有关的操作都会与SecurityManager交互, 且其管理着所有的Subject,可以看出它是Shiro的核心,负责与Shiro的其他组件进行交互, 相当于SpringMVC中的DispatcherServlet的角色。

        Realm:域,Shiro从Realm获取安全数据(如用户、角色、权限), 就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法,也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作,可以把Realm看成DataSource。

2.5. Shiro架构(Shiro内部来看)

        安全认证框架之Shiro详解_第19张图片
        Subject: 主体,任何可以与应用交互的“用户”,接收客户端账号密码,有一系列的认证授权方法。

        Security Manager: 所有具体的交互都通过SecurityManager进行控制,它管理着所有的Subject,并负责进行认证、授权、会话及缓存的管理。

        Authenticator:负责Subject认证,是一个扩展点,可以自定义实现,可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过。最简单可以理解为对用户的账号密码进行校验。

        Authorizer:授权器,即访问控制器,用来决定主体是否有权限进行相应的操作,即控制着用户能访问应用中的哪些功能。

        Realm: 可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的,可以是JDBC实现,也可以是内存实现等等。由用户提供,所以一般在应用中都需要实现自己的Realm。

        Session Manager:管理Session生命周期的组件,而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境。类似于Web session域的session管理域。

        Cache Manager:缓存控制器,用来管理如用户、角色、权限等的缓存的,因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能。

        Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密。

3.Hello World

        使用Maven搭建一个quickstart工程(Java工程)

3.1. 导入依赖包


<dependency>
  <groupId>org.apache.shirogroupId>
  <artifactId>shiro-allartifactId>
  <version>1.7.0version>
dependency>


<dependency>
  <groupId>org.slf4jgroupId>
  <artifactId>slf4j-log4j12artifactId>
  <version>1.7.30version>
dependency>

<dependency>
  <groupId>org.slf4jgroupId>
  <artifactId>slf4j-apiartifactId>
  <version>1.7.30version>
dependency>

<dependency>
  <groupId>org.slf4jgroupId>
  <artifactId>jcl-over-slf4jartifactId>
  <version>2.0.0-alpha1version>
dependency>

<dependency>
  <groupId>log4jgroupId>
  <artifactId>log4jartifactId>
  <version>1.2.17version>
dependency>

注:上面日志包版本过新会引起Null异常和ClassNotFoundException。

3.2. 创建log4j.properties及shiro.ini文件

在resources下创建log4j.properties及shiro.ini文件。
在Shiro官网下载包:
在这里插入图片描述
解压后打开samples目录,复制quickstart示例中的log4j.properties和shiro.ini文件。
安全认证框架之Shiro详解_第20张图片
log4j.properties代码:

log4j.rootLogger=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

# General Apache libraries
log4j.logger.org.apache=WARN

# Spring
log4j.logger.org.springframework=WARN

# Default Shiro logging
log4j.logger.org.apache.shiro=INFO

# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

shiro.ini代码:

#用户名 = 密码,角色1,角色2...,角色n    
[users]
root = 123456, admin
guest = 123456, guest
test = 123456, role1, role2

# -----------------------------------------------------------------------------
# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
# 角色名=权限1,权限2...权限n
# -----------------------------------------------------------------------------
[roles]
admin = *
guest = guest
role1 = perm1,perm2
role2 = perm3

3.3. 创建Shiro测试类

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.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ShiroTest {
    private static final transient Logger log = LoggerFactory.getLogger(ShiroTest.class);
    public static void main(String[] args) {
        //1. 这里的SecurityManager是org.apache.shiro.mgt.SecurityManager
        // 而不是java.lang.SecurityManager
        // 加载配置文件
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath: shiro.ini");
        //2.解析配置文件,并且返回一些SecurityManger实例
        SecurityManager securityManager = factory.getInstance();
        //3.将SecurityManager绑定给SecurityUtils
        SecurityUtils.setSecurityManager(securityManager);
        // 获取当前Subject,安全操作,Subject是当前登录的用户
        Subject currentUser = SecurityUtils.getSubject();
        // 获取应用的当前会话,并设置属性
        Session session = currentUser.getSession();
        //放进去一个key和一个value
        session.setAttribute("someKey", "aValue");

        //根据key拿到value
        String value = (String) session.getAttribute("someKey");
        if ("aValue".equals(value)) {//比较拿到的值和原来的值是否一致
            log.info("检索到正确的值[" + value + "]");
        }

        //尝试进行登录用户,如果登录失败了,我们进行一些处理
        if (!currentUser.isAuthenticated()) {//用户没有登录过
            UsernamePasswordToken token = new UsernamePasswordToken("test", "123456");
            token.setRememberMe(true);//是否记住用户
            try {
                currentUser.login(token);
                //当我们获登录用户之后
                log.info("用户 [" + currentUser.getPrincipal() + "] 登陆成功");
                // 查看用户是否有指定的角色
                if (currentUser.hasRole("admin")) {
                    log.info("您有admin角色");
                } else {
                    log.info("您没有admin角色");
                }
                if (currentUser.hasRole("role1")) {
                    log.info("您有role1角色");
                } else {
                    log.info("您没有role1角色");
                }

                // 查看用户是否有某个权限
                if (currentUser.isPermitted("perm1")) {
                    log.info("您有perm1权限");
                } else {
                    log.info("您没有perm1权限");
                }
                if (currentUser.isPermitted("guest")) {
                    log.info("您有guest权限");
                } else {
                    log.info("您没有guest权限");
                }
                //登出
                currentUser.logout();
            } catch (UnknownAccountException uae) {
                log.info(token.getPrincipal() + "账户不存在");
            } catch (IncorrectCredentialsException ice) {
                log.info(token.getPrincipal() + "密码不正确");
            } catch (LockedAccountException lae) {
                log.info(token.getPrincipal() + "用户被锁定了 ");
            } catch (AuthenticationException ae) {
                //无法判断是什么错了
                log.info(ae.getMessage());
            }
        }
    }
}

        在Shiro中,用户需要提供principals(身份)和 credentials(证明)给Shiro,从而应用能验证用户身份。
        Principals:身份, 即主体的标识属性,如用户名、邮箱、手机号等,唯一即可。
        Credentials:证明/凭证, 即只有主体知道的安全值,如密码、数字证书等。
俗称:用户名与密码

从如上代码可总结出身份验证的步骤:
(1)收集用户身份/凭证,即如用户名/密码;
(2)调用Subject.login()进行登录, 如果失败将得到相应的AuthenticationException异常,根据异常提示用户错误信息;否则登录成功。
(3)AuthenticationException表示身份验证失败,其子类:
        DisableAccountException 禁用的账号
        LockedAccountException 锁定的账号
        UnknownAccountException 错误的账号
        ExcessiveAttemptsException 登录失败次数过多
        IncorrectCredentialsException 错误的凭证
        ExpiredCredentialsException 过期的凭证
(4)最后通过调用Subject.logout()进行退出操作。

问题:
(1)用户名/密码硬编码在shiro.ini配置文件中,以后需要改成如数据库存储,且密码需要加密存储。
(2)用户身证Token可能不仅仅是用户名/密码,也可能还有其他的,如登录时允许用户名/邮箱/手机号码同时登录。

3.4 下一篇:安全认证框架Shiro之认证

你可能感兴趣的:(技术分享,安全,java)