Shiro超详细学习笔记(附源码)

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
访问控制,不限时长
简介: Shiro超详细学习笔记(附源码)

Shiro超详细学习笔记(附源码)

引言:

      本文主要分享了Shiro和权限管理相关的知识,包括权限管理的简介、权限认证、权限授权、权限系统(演变思路)、基于URL的拦截思路、Shiro的简介、Shiro的内部结构(7板块)、初始的Shiro案例、带有角色权限的案例、自定义安全策略的shiro应用案例、自定义安全策略的shiro加入MD5的应用案例(均附源码);


@[toc]

在分享Shiro前先说说权限管理

1. 权限管理

1.1 权限管理简介

       根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源不多不少;权限包括用户认证授权两部分,对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问;

每个系统都有权限管理,只不过力度不同

功能:权限验证、用户注册、登录、分配权限、角色管理

授权(是否有权限,权限校验)、认证(处理登录)、加密、会话(用户状态信息的管理)

安全管理器

  • 认证管理器
  • 权限管理器
  • Session管理器
  • 加密
  • Realms访问数据源的组件(访问用户数据源)

1.2 认证

       认证就是判断一个用户是否是合法用户的一个过程,通过核对相应的信息,来与系统中的信息进行比较是否一致,其中包括用户名密码、指纹、人脸、二维码、刷卡......

认证流程:

  1. 访问系统资源
  2. 判断是否可以匿名访问(可以继续访问,不可以进行下一步)
  3. 判断用户是否登录(登录继续访问,没有登录进行下一步)
  4. 输入用户名密码进行身份认证
  5. 判断是否通过(通过认证继续访问,没有通过继续认证)

在这里插入图片描述

1.3 授权

       授权就是访问控制,控制谁能访问那些资源,相当于检票的过程,访问控制就是主体进行身份认证后,需要分配权限,没有权限不可以访问;

授权流程:

  1. 访问系统资源
  2. 进行身份认证
  3. 判断是否通过认证(没有通过继续认证,通过进行下一步)
  4. 权限控制(从数据拿出,分配权限)
  5. 判断是否拥有访问权限(有权限继续访问,没有权限拒绝访问)

在这里插入图片描述

资源访问分为三级:

  • 匿名可访问
  • 认证可访问
  • 拥有特定权限可访问

1.4 权限系统

1.4.1 涉及的对象及关系(7张表)

       我们知道对象就是实体,包括用户、权限、资源、角色

权限系统涉及的关系:

  • 用户-->权限:多对多
  • 用户-->角色:多对多
  • 角色-->权限:多对多
  • 权限-->资源:多对一(一个资源多个权限增删查改)

1.4.2 权限管理的设计演变思路

  1. 七表:用户、角色、权限、资源各一张表,用户与角色之间有用户角色的关系表、角色权限之间有角色权限表、用户和权限有一张表;
  2. 六表:在七表的基础上省略了用户和权限之间的表,直接将权限授予角色,角色在授予用户;(一个用户可以单独授权又可以是某个角色)
  3. 五表:将多对一变为一对一,权限和资源融合,简化为五张表;

1.4.3 权限控制

1.4.3.1 基于角色的访问控制

RBAC基于角色的访问控制(Role-Based Access Control)以角色为中心进行访问控制;

缺点:

  • 系统扩展性较差
  • 以角色进行访问控制粒度较粗

1.4.3.2 基于资源的访问控制

RBAC基于资源的访问控制(Resouce-Based Access Control)以资源为中心进行访问控制;

优点:

  • 系统设计时定义好权限标识
  • 系统可扩展性强

1.5 基于URL拦截

       基于url拦截是企业中常用的权限管理方法,将系统操作的每个url配置在权限表中,将权限对应到角色,将角色分配给用户,用户访问系统功能通过Filter进行过滤,Filter获取用户访问的url,只要访问的url是用户分配角色中的url则放行继续访问;

URL拦截的流程:

  1. 访问系统资源
  2. 获取访问的url
  3. 判断url是否是公开地址(是继续访问,不是进行下一步)
  4. 判断是否存在session(没有输入用户名密码,进行身份认证,直到成功并将用户信息url记录到session中;存在进行下一步)
  5. 授权过滤器拦截获取访问url
  6. 判断url是否是公开地址(是:继续访问;不是:进行下一步)
  7. 判断url是否是只要登录就可以访问,无需分配权限(是:继续访问;不是:进行下一步)
  8. 判断url是否存在权限(存在:放行继续访问;不存在提示无权访问)

在这里插入图片描述

2. Shiro

2.1 Shiro简介以及内部结构

        Shiro是一个Java的安全框架,提供了认证、授权、加密和会话管理功能;是开发权限的快速框架,应用广泛(Web、非Web、集群分布式应用)

Shiro内部结构

  • Subject:请求主体,外部应用与 subject进行交互,subject记录当前操作用户,用户就是当前操作的主体;外部程序通过subject进行认证授权,subject通过Security Manager安全管理器进行认证授权
  • SecurityManager:安全管理器,相当于SpringMVC中的DispatcherServlet是Shiro的心脏;所有的交互都通过SecurityManager进行控制,管理着所有Subject负责进行认证和授权、会话及缓存的管理;是一个接口,继承了Authenticator、Authorizer、SessionManager这三个接口
  • Authenticator:认证器,负责主体(subject)认证的,是一个接口,shiro提供了ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数的需求,也可以自定义认证器
  • Authorizer:授权器、访问控制器,用来决定主体是否有权限进行相应的操作,即控制着用户能访问应用中的哪些功能,在访问功能时需要通过授权器判断用户是否有此功能的操作权限;
  • Realm:域,可以有1个或多个Realm;是安全实体数据源,用于获取安全实体的,Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;realm不只是从数据源取数据,在realm中还有认证授权校验相关的代码;
  • SessionManager:会话管理,shiro框架提供了一套会话管理,它不依赖web容器的session,自己抽象了一个自己的Session来管理主体与应用之间交互的数据,所以shiro可以使用在非web环境中,也可以将分布式应用的会话集中在一点管理,可使它实现单点登录
  • SessionDAO:会话管理DAO,是对session会话管理操作的一套接口,比如要将session存储到数据库,可以使用jdbc将会话存储到数据库;此外SessionDAO中可以使用Cache进行缓存,以提高性能;
  • CacheManager:缓存管理,将用户权限存储在缓存,可以提高性能;
  • Crypography:密码管理,Shiro提供了一套加密/解密的组件,方便开发。提供了常用的散列、加/解密算法;

2.2 环境的搭建

新建普通的Maven项目

在pom文件中导入依赖

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.3.2</version>
        <exclusions>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.25</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>commons-httpclient</groupId>
        <artifactId>commons-httpclient</artifactId>
        <version>3.1</version>
    </dependency>
</dependencies>

2.3 简单的Shiro测试案例

2.3.1 配置初始化文件

在resources文件下配置初始化文件——shiro-first.ini

#配置用户名,带用户数据表
[users]
kaka=1234
taotao=4567

2.3.2 编写测试文件

在test文件下编写测试类

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
/**
 * Created by Kak on 2020/9/2.
 */
public class TestShiroBase {
   
   
    @Test
    //认证处理
    public void testShiroFirst(){
   
   
        //根据ini初始化文件创建SecurityManager工厂
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-first.ini");
        //生成安全管理器,创建securityManager实例
        SecurityManager securityManager = factory.createInstance();
        //使用shiro提供的工具装配安全管理器
        SecurityUtils.setSecurityManager(securityManager);
        //获取shiro访问的主体对象
        Subject subject = SecurityUtils.getSubject();
        //模拟用户登录操作创建访问主体的令牌
        AuthenticationToken token = new UsernamePasswordToken("kaka", "1234");
        try{
   
   
            subject.login(token);
        }catch (IncorrectCredentialsException icex){
   
   
            System.out.println("用户口令错误!!!");
        }catch (UnknownAccountException uaEx){
   
   
            System.out.println("用户名错误!!!");
        }catch (AuthenticationException aex){
   
   
            System.out.println("用户认证失败!!!");
        }
        if (subject.isAuthenticated()){
   
   
            System.out.println("用户登录成功!!!");
        }
    }
}

2.3.3 运行结果

在这里插入图片描述

2.4 带角色权限的认证

2.4.1 配置初始化文件

在resources文件下配置初始化文件——shiro-perms.ini

[users]
kaka=1234,role1,role2
taotao=12345,role2,role3
[roles]
role1=stu:select,stu:insert
role2=stu:update,stu:delete
role3=emp:select

2.4.2 编写测试文件

在test文件下编写测试类

 @Test
    //加入权限
    public void testShiroPerms(){
   
   
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-perms.ini");
        SecurityManager securityManager = factory.createInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("kaka","1234");
        try{
   
   
            subject.login(token);
        }catch (AuthenticationException aex){
   
   
            System.out.println("用户认证错误,登录失败!!!");
        }
        if(subject.isAuthenticated()){
   
   
            System.out.println("用户登录成功!!!");
            if(subject.isPermitted("stu:select")){
   
   
                System.out.println("用户有查询权限stu:select");
            }else{
   
   
                System.out.println("无权访问stu:select!!!");
            }
            String[] perms = {
   
   "stu:select","stu:delete"};
            boolean[] permitted = subject.isPermitted(perms);//我们需要判断的权限
            if (permitted[1]){
   
   
                System.out.println("用户有权访问stu:delete");
            }
            if(!subject.isPermittedAll(perms)){
   
   
                System.out.println("用户无权访问!!!");
            }
            if (subject.hasRole("role1")){
   
   
                System.out.println("kaka有role1角色");
            }
        }
    }

2.4.3 运行结果

在这里插入图片描述

2.5 自定义realm的shiro案例

2.5.1 配置初始化文件

在resources文件下配置初始化文件——shiro-realm.ini

[main]
#自定义realm
myRealm=com.sx.kak.shiro.MyRealm
#在securityManager中设置自定义的realm
securityManager.realms=$myRealm

2.5.2 自定义安全策略

创建shiro包,在包下创建MyRealm.java

package com.sx.kak.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.HashSet;
import java.util.Set;

/**
 * Created by Kak on 2020/9/2.
 */
public class MyRealm extends AuthorizingRealm{
   
   
    //shiro中的授权实现
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
   
   
        Object principal = principalCollection.getPrimaryPrincipal();
        //根据用户信息查询数据库中的权限
        String userName = (String) principal;
        //创建用户授权信息对象
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //创建权限集合
        Set<String> perms = new HashSet<String>();
        perms.add("stu:select");
        perms.add("stu:update");
        authorizationInfo.setStringPermissions(perms);
        return authorizationInfo;
    }
    //shiro中的认证实现
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
   
   
        //获取用户信息
        String username = (String)authenticationToken.getPrincipal();
        //获取密码
        Object credentials = authenticationToken.getCredentials();
        System.out.println("userName:" + username+"password:"+credentials);
        //根据用户名查询用户对象信息获取用密码
        String userName="kaka";
        String password="1234";
        //将送来用户账号及根据账号查出的密码(凭证)封装成一个AuthenticationInfo对象,返回
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, password, getName());
        //将外部获取的用户信息及凭证信息 封装送给Authenticator对象进行认证
        return simpleAuthenticationInfo;
    }
}

2.5.3 编写测试文件

在test文件下编写测试类

@Test
    //自定义安全策略的shiro
    public void testShiroRealm(){
   
   
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");
        SecurityManager securityManager = factory.createInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("kaka", "1234");
        try{
   
   
            subject.login(token);
        }catch (UnknownAccountException uaex){
   
   
            System.out.println("账户名不存在!!!");
        }catch (IncorrectCredentialsException ice){
   
   
            System.out.println("凭证错误!!!");
        }catch (AuthenticationException ae){
   
   
            System.out.println("认证失败!!!");
        }
        if(subject.isAuthenticated()){
   
   
            System.out.println("用户登录成功!!!");
        }else{
   
   
            System.out.println("用户登录失败!!!");
        }
        if(subject.isPermittedAll("stu:select")){
   
   
            System.out.println("kaka用户有权访问stu:select");
        }else{
   
   
            System.out.println("kaka用户无权访问stu:select");
        }
    }

2.5.4 运行结果

在这里插入图片描述

2.6 MD5密钥

一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致;

//生成MD5密钥
    public void testMd5(){
   
   
        String pwd = "1234";
        String salt = "kaka";
        Md5Hash md5Hash1 = new Md5Hash(pwd);
        String md5Str1 = md5Hash1.toString();
        System.out.println("md5加密:" + md5Str1);
        Md5Hash md5Hash2 = new Md5Hash(pwd, salt, 1);
        String md5Str2 = md5Hash2.toString();
        System.out.println("加盐:"+md5Str2);
        Md5Hash md5Hash3 = new Md5Hash(pwd, salt, 1024);
        String md5Str3 = md5Hash3.toString();
        System.out.println("多次加盐:"+md5Str3);
    }
md5加密:81dc9bdb52d04dc20036dbd8313ed055
加盐:4fa51ff55b001ea9b7c55338b76834f7
多次加盐:eaee658c75dc83917d7be1bd689ff15e

在这里插入图片描述

2.7 自定义realm的shiro加入MD5案例

2.7.1 配置初始化文件

[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#设置散列算法
credentialsMatcher.hashAlgorithmName=md5
#设置散列次数
credentialsMatcher.hashIterations=1
#将凭证匹配器设置到realm
myRealm=com.sx.kak.shiro.MyRealmMd5
#在securityManager中设置自定义的realm
securityManager.realms=$myRealm
myRealm.credentialsMatcher=$credentialsMatcher

2.7.2 自定义安全策略(MD5)

创建shiro包,在包下创建MyRealmMd5.java

package com.sx.kak.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

/**
 * Created by Kak on 2020/9/2.
 */
public class MyRealmMd5 extends AuthorizingRealm{
   
   
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
   
   
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
   
   
        //用户名
        String principal = (String)authenticationToken.getPrincipal();
        //根据用户名查询用户信息(密码)
        String hashedCredentials ="4fa51ff55b001ea9b7c55338b76834f7";
        ByteSource credentialsSalt = ByteSource.Util.bytes("kaka");
        /**
         * principal:用户信息
         * hashedCredentials:加密之后的密文
         * credentialsSalt:加密时加的盐
         * AuthorizingRealm 的派生类名称
         */
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, getName());
        return authenticationInfo;
    }
}

2.7.3 编写测试文件

在test文件下编写测试类

@Test
    //自定义安全策略的shiro加密钥
    public void testShiroMd5(){
   
   
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-realm-md5.ini");
        SecurityManager securityManager = factory.createInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("kaka", "1234");
        try{
   
   
            subject.login(token);
        }catch (UnknownAccountException uaex){
   
   
            System.out.println("账户名不存在!!!");
        }catch (IncorrectCredentialsException ice){
   
   
            System.out.println("凭证错误!!!");
        }catch (AuthenticationException ae){
   
   
            System.out.println("认证失败!!!");
        }
        if(subject.isAuthenticated()){
   
   
            System.out.println("用户登录成功!!!");
        }else{
   
   
            System.out.println("用户登录失败!!!");
        }
    }

2.7.4 运行结果

图片.png

目录
相关文章
|
Java Maven
Shiro学习-快速入门(二)
Shiro学习-快速入门(二)
70 0
|
存储 算法 数据安全/隐私保护
【Shiro】第三章 Shiro入门(三)
【Shiro】第三章 Shiro入门(三)
79 1
|
安全 数据安全/隐私保护
【Shiro】第三章 Shiro入门(一)
【Shiro】第三章 Shiro入门
77 1
|
存储 算法 程序员
【Shiro】第三章 Shiro入门(二)
【Shiro】第三章 Shiro入门(二)
81 1
|
缓存 数据库 数据安全/隐私保护
【Shiro】第三章 Shiro入门(四)
【Shiro】第三章 Shiro入门(四)
82 0
|
存储 缓存 安全
Shiro学习之Shiro简介
Shiro学习之Shiro简介
107 0
|
Java 数据库 数据安全/隐私保护
Shiro学习之Shiro基本使用(2)
Shiro学习之Shiro基本使用(2)
57 0
|
Java 网络安全 数据库
shiro实战教程(二)
shiro实战教程(二)
235 1
shiro实战教程(二)
|
SQL 缓存 NoSQL
shiro实战教程(三)
shiro实战教程(三)
129 0
shiro实战教程(三)
|
存储 缓存 安全
shiro实战教程(一)
shiro实战教程(一)
159 0
shiro实战教程(一)