springboot整合shiro理论到实战(由简入凡)

springboot整合shiro

1. Shiro权限实战介绍

1.1 Shiro权限实战介绍

在阅读之前建议先去官网把一些基本概念看了
http://shiro.apache.org/get-started.html
在这里插入图片描述

简介:为什么要学Shiro权限框架

  • 公司新项目需要用到、要么就是需要接收别人的代码、个人技术栈的成长
  • Springboot2.x/SpringMVC + Maven + jdk8 + IDEA/Eclipse
  • 学后水平:掌握Shiro在公司中实际的使用,包括明白里面的核心原理

1.2 权限控制和初学JavaWeb处理访问权限控制

简介:什么是权限控制,初学JavaWeb时处理流程

  • 什么是权限控制:
    • 忽略特别细的概念,比如权限能细分很多种,功能权限,数据权限,管理权限等
    • 理解两个概念:用户和资源,让指定的用户,只能操作指定的资源(CRUD)
  • 初学javaweb时怎么做
    • Filter接口中有一个doFilter方法,自己编写好业务Filter,并配置对哪个web资源进行拦截后
    • 如果访问的路径命中对应的Filter,则会执行doFilter()方法,然后判断是否有权限进行访问对应的资源
    • /api/user/info?id=1
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws Exception {
   
   
        HttpServletRequest httpRequest=(HttpServletRequest)request;
        HttpServletResponse httpResponse=(HttpServletResponse)response;
        
        HttpSession session=httpRequest.getSession();
        
        if(session.getAttribute("username")!=null){
   
   
            chain.doFilter(request, response);
        } else {
   
   
            httpResponse.sendRedirect(httpRequest.getContextPath()+"/login.jsp");
        }
        
    }

2. 大话权限框架核心知识ACL和RBAC

2.1 权限框架设计之ACL和RBAC

  • ACL: Access Control List 访问控制列表

    • 以前盛行的一种权限设计,它的核心在于用户直接和权限挂钩
    • 优点:简单易用,开发便捷
    • 缺点:用户和权限直接挂钩,导致在授予时的复杂性,比较分散,不便于管理
    • 例子:常见的文件系统权限设计, 直接给用户加权限
  • RBAC: Role Based Access Control

    • 基于角色的访问控制系统。权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限
    • 优点:简化了用户与权限的管理,通过对用户进行分类,使得角色与权限关联起来
    • 缺点:开发对比ACL相对复杂
    • 例子:基于RBAC模型的权限验证框架与应用 Apache Shiro、spring Security
  • BAT企业 ACL,一般是对报表系统,阿里的ODPS

  • 总结:不能过于复杂,规则过多,维护性和性能会下降, 更多分类 ABAC、PBAC等

2.2 主流权限框架介绍和技术选型

简介:介绍主流的权限框架 Apache Shiro、spring Security

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoCDI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
一句话:Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架

什么是 Apache Shiro:官网基础介绍

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
一句话:Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能

两个优缺点,应该怎么选择

  • Apache ShiroSpring Security , 前者使用更简单

  • Shiro 功能强大、 简单、灵活, 不跟任何的框架或者容器绑定,可以独立运行

  • Spring SecuritySpring 体系支持比较好,脱离Spring体系则很难开发

  • SpringSecutiry 支持Oauth鉴权 https://spring.io/projects/spring-security-oauthShiro需要自己实现

  • 总结:两个框架没有谁超过谁,大体功能一致,新手一般先推荐Shiro,学习会容易点

3.Apache Shiro基础概念知识和架构

3.1 Shiro核心知识之架构图交互和四大模块

  • 直达Apache Shiro官网 http://shiro.apache.org/introduction.html
    图片来源于官网
    img
    重点:记住下面这四个概念
  • 什么是身份认证
    • Authentication,身份认证,一般就是登录
  • 什么是授权
    • Authorization,给用户分配角色或者访问某些资源的权限
  • 什么是会话管理
    • Session Management, 用户的会话管理员,多数情况下是web session
  • 什么是加密
    • Cryptography, 数据加解密,比如密码加解密等

3.2 用户访问Shrio权限控制运行流程和常见概念

简介:讲解用户访问整合Shrio的系统,权限控制的运行流程和Shiro常见名称讲解

img

重点:记住下面这七个概念

  • Subject
    • 我们把用户或者程序称为主题(如用户,第三方服务,cron作业),主题去访问系统或者资源,
      主题(subject)由主体(principal)和凭证(credential)构成,也就是用户名和密码。
  • SecurityManager
    • 安全管理器,Subject的认证和授权都要在安全管理器下进行
  • Authenticator
    • 认证器,主要负责Subject的认证
  • Realm
    • 数据域,Shiro和安全数据的连接器,好比jdbc连接数据库; 通过realm获取认证授权相关信息
  • Authorizer
    • 授权器,主要负责Subject的授权, 控制subject拥有的角色或者权限
  • Cryptography
    • 加解密,Shiro的包含易于使用和理解的数据加解密方法,简化了很多复杂的api
  • Cache Manager
    • 缓存管理器,比如认证或授权信息,通过缓存进行管理,提高性能

更多资料导航:http://shiro.apache.org/reference.html

4.Springboot2.x整合 Apache Shiro快速上手实战

4.1 SpringBoot2.x整合Shiro权限认证项目搭建

简介:使用SpringBoot2.x整合Shiro权限认证

  • Maven + Jdk8 + Springboot 2.X + IDEA(Eclipse也可以)

步骤极少

  • 创建SpringBoot项目
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!--<scope>runtime</scope>-->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        	<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.4.0</version>
		</dependency>

4.2 快速上手之Shiro认证和授权流程实操

简介:Shrio的认证和授权实操

认证:用户身份识别,俗称为用户“登录”
在测试包下创建测试类

package com.xdclass;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

/**
 * @author : wly
 * @version : 1.0
 * @date : 2021/11/20 10:14
 * @description:
 */
public class QuickStartTest {
   
   

    private DefaultSecurityManager securityManager = new DefaultSecurityManager();
    private SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    @Before
    public void init() {
   
   

        //初始化数据源,相当于往数据库存两个用户
        simpleAccountRealm.addAccount("user","1234");
        simpleAccountRealm.addAccount("jack","123");
        //构建环境,把realm添加到shiro环境里
        securityManager.setRealm(simpleAccountRealm);
    }

    @Test
    public void testAuthentication() {
   
   
    //设置securityManager
        SecurityUtils.setSecurityManager(securityManager);
        //获取用户主体
        Subject subject = SecurityUtils.getSubject();
        //用户输入的用户名和密码
        UsernamePasswordToken token = new UsernamePasswordToken("user","1234");
        //用户登录
        subject.login(token);
        System.out.println("身份认证结果:"+subject.isAuthenticated());
    }
}

选中loginctrl+alt+B可以看到login方法的实现,可以看到调用的是
Subject subject = securityManager.login(this, token);
这个login再往深了看就是实现身份认证。
在这里插入图片描述

运行测试方法,结果为true表示认证成功。
在这里插入图片描述

在这里插入图片描述
更细节的认证流程参考以下博客,看了直接懂
https://blog.csdn.net/yanluandai1985/article/details/79171389

4.3 Shiro认证和授权流程和常用API梳理

简介:讲解Shiro的授权实操和常用Api 梳理
创建新的测试类 QuickStartTest2

package com.xdclass;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

/**
 * @author : wly
 * @version : 1.0
 * @date : 2021/11/20 10:14
 * @description:
 */
public class QuickStartTest2 {
   
   
    private DefaultSecurityManager securityManager = new DefaultSecurityManager();
    private SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    @Before
    public void init() {
   
   

        //初始化数据源
        simpleAccountRealm.addAccount("user","1234","root","admin");
        simpleAccountRealm.addAccount("jack","123","user");
        //构建环境
        securityManager.setRealm(simpleAccountRealm);
    }

    @Test
    public void testAuthentication() {
   
   
        SecurityUtils.setSecurityManager(securityManager);
        //用户主题(由principal(主体,即用户名)和credential(凭证,即密码)构成)
        Subject subject = SecurityUtils.getSubject();
        //模拟用户输入的用户名和密码
        UsernamePasswordToken token = new UsernamePasswordToken("user","1234");
        //用户登录
        subject.login(token);
        System.out.println("身份认证结果:"+subject.isAuthenticated());
        System.out.println("主体是:"+subject.getPrincipal());
        System.out.println("是否有root角色"+subject.hasRole("root"));
        //用户退出登录
        subject.logout();
        System.out.println("身份认证结果:"+subject.isAuthenticated());
    }
}

在这里插入图片描述
我们来看看hasRole具体怎么执行的

subject.hasRole("root") 

ctrl+alb+b点进去,可以看到调用的是DelegatingSubject类的抽象方法hasRole
DelegatingSubject
这个hasRole仍然是抽象的,再次进入hasRole,可以看到调用的AuthorizingSecurityManager类的hasRole方法依然是抽象的

在这里插入图片描述
在这个方法里又调用了hasRole方法,再次使用ctrl+alt+b,可以看到有三个实现方法

在这里插入图片描述
选那个呢,选ModularRealmAuthorizer类的,进去之后发现这个hasRole不是抽象的了,说明就是最终的实现方法了。

在这里插入图片描述
怎么确定这个方法是属于ModularRealmAuthorizer类呢,可以在DelegatingSubject类的hasRole那里return哪行打个断点进行调试,开始dubug按键F7F8F7F8F8
在这里插入图片描述

在这里插入图片描述
hasRole()有返回值,有这个角色返回true,没有返回false.
checkRole()没有返回值。也是用来判断有没有该角色。咋一看好像功能重复,其实它起到了断言的作用,如果用户没有该角色会抛出AuthorizationException,直接不往下走了,而使用hasRole()要达到没有某种角色就抛出异常停止运行需要额外的代码。

5. 详细讲解Apache Shiro realm实战

5.1 Shiro安全数据来源之Realm讲解

简介:讲解shiro默认自带的realm和常见使用方法

  • realm作用:ShiroRealm 获取安全数据
  • 默认自带的realmidea查看realm继承关系
    在这里插入图片描述

有默认实现和自定义继承的realm

  • 两个概念

    • principal : 主体的标示,可以有多个,但是需要具有唯一性,常见的有用户名,手机号,邮箱等
    • credential:凭证, 一般就是密码
    • 所以一般我们说 principal + credential 就账号 + 密码
  • 开发中,往往是自定义realm , 即继承AuthorizingRealm
    为什么这么做,因为AuthorizingRealm支持授权,AuthenticatingRelam支持认证,这不就功能够了吗?
    所以,一般在真实的项目中,我们不会直接实现Realm接口,也不会直接继承最底层的功能贼复杂的IniRealm。我们一般的情况就是直接继承AuthorizingRealm,能够继承到认证与授权功能。它需要强制重写两个方法:doGetAuthorizationInfodoGetAuthenticationInfo

5.2 快速上手之Shiro内置IniRealm实操和权限验证api

建议先看下ini配置,一看就懂的那种。IniRealmRealm的最底层实现类,它从后缀为ini或者其他的文件中读取配置,读取程序员设置的用户和角色等相关信息。但是不用设置SecurityManager,因为读取文件后就会有一个默认的。
ini配置文档:http://shiro.apache.org/configuration.html#Configuration-INIConfiguration
先看下上面这个。

下面开始实际案例
首先在test目录建立一个resources目录(要mark directory as test resources root,不然无法新建文件),里面建一个shiro.ini文件。

# 格式 name=password,role1,role2,..roleN
[users]
# user 'root' with password 'secret' and the 'admin' role,
jack = 456, user
# user 'guest' with the password 'guest' and the 'guest' role
xdclass = 123, root,admin

# 格式 role=permission1,permission2...permissionN   也可以用通配符
# 下面配置user的权限为所有video:find,video:buy,如果需要配置video全部操作crud 则 user = video:*
[roles]
user = video:find,video:buy
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *

这份文件设置了两个用户jack 密码是456,拥有user角色;xdclass密码是123,拥有rootadmin角色。video:find,video:buy表示角色user拥有这两个权限,具体命名去看上面的文档。
admin = *表示角色admin拥有所有权限

创建测试类QuickStartTest5_2

package com.xdclass;
import org.apache.shiro.SecurityUtils;
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.junit.Test;
/**
 * @author : wly
 * @version : 1.0
 * @date : 2021/11/21 11:58
 * @description:
 */
public class QuickStartTest5_2 {
   
   
    @Test
    public void testAuthentication() {
   
   

        //创建SecurityManager工厂,通过ini配置文件读取
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //通过SecurityManager工厂获取SecurityManager实例
        SecurityManager securityManager = factory.getInstance();
        //通过SecurityUtils工具类将SecurityManager设置到运行环境中
        SecurityUtils.setSecurityManager(securityManager);
        //通过SecurityUtils工具类获取主题
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("jack","456");
        subject.login(token);

        System.out.println("身份认证结果:"+subject.isAuthenticated());
        String username = subject.getPrincipal().toString();
        System.out.println("主体是:"+subject.getPrincipal());
        System.out.println("用户 "+username+" 是否有root角色:"+subject.hasRole("root"));
        System.out.println("用户 "+username+" 是否有user角色:"+subject.hasRole("user"));
        System.out.println("用户 "+username+" 是否有video:find 权限:"+subject.isPermitted("video:find"));
        System.out.println("用户 "+username+" 是否有video:buy 权限:"+subject.isPermitted("video:buy"));
        System.out.println("用户 "+username+" 是否有video:find,video:buy 两个权限:"+subject.isPermittedAll("video:find","video:buy"));
        //用户退出登录
        subject.logout();
        System.out.println("身份认证结果:"+subject.isAuthenticated());

    }

    @Test
    public void testAuthentication2() {
   
   
        //创建SecurityManager工厂,通过ini配置文件读取
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //通过SecurityManager工厂获取SecurityManager实例
        SecurityManager securityManager = factory.getInstance();
        //通过SecurityUtils工具类将SecurityManager设置到运行环境中
        SecurityUtils.setSecurityManager(securityManager);
        //通过SecurityUtils工具类获取主题
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("xdclass","123");
        subject.login(token);

        System.out.println("身份认证结果:"+subject.isAuthenticated());
        String username = subject.getPrincipal().toString();
        System.out.println("主体是:"+subject.getPrincipal());
        System.out.println("用户 "+username+" 是否有admin角色:"+subject.hasRole("admin"));
        //在ini设置了admin=* 表示角色admin有所有权限
        System.out.println("用户 "+username+" 是否有video:find,video:buy 两个权限:"+subject.isPermittedAll("video:find","video:buy"));
        //用户退出登录
        subject.logout();
        System.out.println("身份认证结果:"+subject.isAuthenticated());

    }
}

第一个测试方法测试用户jack,第二个测试用户xdclass

5.3 快速上手之Shiro内置JdbcRealm实操

创建一个数据库,导入以下sql文件

DROP TABLE IF EXISTS `roles_permissions`;
CREATE TABLE `roles_permissions` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(100) DEFAULT NULL,
  `permission` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_roles_permissions` (`role_name`,`permission`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES `roles_permissions` WRITE;
/*!40000 ALTER TABLE `roles_permissions` DISABLE KEYS */;

INSERT INTO `roles_permissions` (`id`, `role_name`, `permission`)
VALUES
	(4,'admin','video:*'),
	(3,'role1','video:buy'),
	(2,'role1','video:find'),
	(5,'role2','*'),
	(1,'root','*');

/*!40000 ALTER TABLE `roles_permissions` ENABLE KEYS */;
UNLOCK TABLES;


DROP TABLE IF EXISTS `user_roles`;

CREATE TABLE `user_roles` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) DEFAULT NULL,
  `role_name` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_user_roles` (`username`,`role_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES `user_roles` WRITE;
/*!40000 ALTER TABLE `user_roles` DISABLE KEYS */;

INSERT INTO `user_roles` (`id`, `username`, `role_name`)
VALUES
	(1,'jack','role1'),
	(2,'jack','role2'),
	(4,'xdclass','admin'),
	(3,'xdclass','root');

/*!40000 ALTER TABLE `user_roles` ENABLE KEYS */;
UNLOCK TABLES;

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) DEFAULT NULL
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值