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

简介: 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 数据库
【SpringSecurity】Spring Security 和Shiro对比
【SpringSecurity】Spring Security 和Shiro对比
1142 0
|
SQL 监控 NoSQL
架构师第一课,一文带你玩转 ruoyi 架构
我理解的架构/框架应该有以下功能: 1.满足日常开发功能,如单点登陆、消息队列、监控等; 2.规范开发者的开发,指定代码格式、注释等; 3.提高开发效率,提供一系列的封装方法,并减少bug的产生率。 下文将详细介绍ruoyi框架。
8054 1
架构师第一课,一文带你玩转 ruoyi 架构
|
安全 API 测试技术
shiro实战系列(十)之Subject
毫无疑问,在 Apache Shiro 中最重要的概念就是 Subject。'Subject'仅仅是一个安全术语,是指应用程序用户的特定 安全的“视图”。一个 Shiro Subject 实例代表了一个单一应用程序用户的安全状态和操作。
1842 0
|
10月前
|
安全 Java 数据安全/隐私保护
适合才最美:Shiro安全框架使用心得
大家好,我是 V 哥。Apache Shiro 是一个强大且灵活的 Java 安全框架,专注于认证、授权、会话管理和加密功能。它常用于保护 Java 应用的访问控制,特别是在 Web 应用中。相比 Spring Security,Shiro 设计更简洁,适合轻量级应用,并且在许多方面具有更好的易用性和扩展性。本文将介绍 Shiro 的核心概念、主要功能和优势,并通过一个典型的 Shiro 应用来展示其基本使用方法。
410 4
|
11月前
|
Java API Apache
Springboot+shiro,完整教程,带你学会shiro
这篇文章提供了一个完整的Apache Shiro与Spring Boot结合使用的教程,包括Shiro的配置、使用以及在非Web和Web环境中进行身份验证和授权的示例。
605 2
Springboot+shiro,完整教程,带你学会shiro
|
11月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
2273 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
|
11月前
|
安全 Java 程序员
shiro学习三:shiro的源码分析
这篇文章是关于Apache Shiro安全框架的源码分析,主要探讨了Shiro的认证流程和自定义Realm的实现细节。
165 0
shiro学习三:shiro的源码分析
|
SQL 前端开发 Java
springboot项目中使用shiro实现用户登录以及权限的验证
这篇文章详细介绍了如何在Spring Boot项目中集成Apache Shiro框架来实现用户登录和权限验证,包括项目依赖配置、数据库连接、实体类定义、控制器、服务层、Mapper层以及前端页面的实现,并展示了实际效果和过滤器代码。
springboot项目中使用shiro实现用户登录以及权限的验证
|
安全 搜索推荐 Java
SpringSecurity扩展用户身份信息(UserDetails)的方式
通过上述步骤,你就能在Spring Security中扩展 `UserDetails`,进而实现更加个性化和复杂的用户认证和授权机制。记住,在添加更多字段时,保持系统安全性的同时,也需要考虑到用户隐私的保护。
1133 1
|
JSON 安全 前端开发
Shiro整合JWT实现认证和权限鉴定(执行流程清晰详细)
一、前情提要 JWT:服务端根据规范生成一个令牌(token),并且发放给客户端(保存在客户端)。此时客户端请求服务端的时候就可以携带者令牌,以令牌来证明自己的身份信息。 Shiro:Java的一个安全(权限)框架,用户登录时把身份信息(用户名/手机号/邮箱地址等)和凭证信息(密码/证书等)封装成一个Token令牌,通过安全管理器中的认证器进行校验,成功则授权以访问系统.(详细描述可以参考文章:Shiro基础)