web后端-Shiro

参考资料

【涛哥】最适合小白入门的Shiro教程

在B站看视频,声音小怎么办?

跟我学Shiro

什么是权限管理?

不同身份的用户进入到系统所能够完成的操作是不相同的,我们对不同用户进行的可执行的操作的管理称之为权限管理。

例如下图职员的权限存在一定交集。

如何实现权限管理?

权限管理设计

  • 基于主页的权限管理(不同用户使用不同的主页,权限通过主页功能菜单进行限制)适用于权限管理比较单一、用户少、每类用户权限固定。

  • 基于用户和权限的权限管理,但是不够灵活,如果新增了100个用户,每个用户都去手动分配权限,太麻烦了。

  • 添加角色表,新增用户时,只需要为其设置角色即可,无需依次设置权限,而对应角色的权限统一设置。如果想为某些用户设置额外的权限,就需要添加新的表。

  • 添加一张额外授权表,即拥有相同角色的某些用户拥有额外的权限

RABC权限管理

为了达成不同账号(员工、总裁)登录系统后看到不同页面,执行不同功能,得出了RBAC(Role-Based Access control)权限模型,就是根据角色的权限,分配可视页面。

三个关键点:

  • 用户 :使用系统的人
  • 角色:使用系统的人是什么职位(员工、经理、总裁)
  • 权限点:职位可以做的事情(左侧菜单栏中的功能模块——>增删改查)

认证权限流程

传统方法

  • 认证:对用户的身份进行检查(登录验证)。
  • 授权:对用户的权限进行检查(是否有对应的操作权限)

流程示意图:

核心思想通过过滤器+session实现

安全框架

帮助我们完成用户身份认证及权限检查功能框架

常用的安全框架:

  • Shiro: Apache Shiro是一个功能强大并且易用的Java安全框架(小而简单)。
  • Spring Security:基于Spring的一个安全框架,依赖Spring
  • OAuth2:第三方授权登录
  • 自定义安全认证中心

Shiro

Apache Shiro是一个功能强大并且易用的Java安全框架,适用于非分布式应用,因为Shiro是基于Session的,而多台服务器Session无法共享。

Shiro可以完成用户认证、授权、密码及会话管理。

Shiro工作原理

Shiro的核心功能

  • Anthentication认证,验证用户是否有相应的身份-登录认证;
  • Authorization授权,即权限验证;对已经通过认证的用户检查是否具有某个权限或者角色,从而控制是否能够进行某种操作;
  • Session Managment会话管理,用户在认证成功之后创建会话,在没有退出之前,当前用户的所有信息都会保存在这个会话中;可以是普通的JavaSE应用,也可以是web应用;
  • Cryptography加密,对敏感信息进行加密处理,shiro就提供这种加密机制;

其它特性

  • Web Support - Shiro提供了过滤器,可以通过过滤器拦截web请求来处理web应用的访问控制
  • Caching缓存支持,shiro可以缓存用户信息以及用户的角色权限信息,可以提高执行效率
  • Concurrency shiro支持多线程应用
  • Testing提供测试支持
  • Run As 允许一个用户以另一种身份去访问
  • Remeber Me

核心组件

  • Subject表示待认证和授权的用户,即用户的账号和密码封装到Subject中,代表某个用户,让Shiro使用Subject进行认证。Subject由帮助
  • SecurityUtil获得。SecurityUtil由Security Manager管理。
  • Security Manager,它是Shiro框架的核心,Shiro就是通过Security Manager来进行内部实例的管理,并通过它来提供安全管理的各种服务。
    • Authenticator,认证器
    • Anthorizer,授权器
    • SessionManager,会话管理器
    •  CacheManager,缓存管理器
  • Realm,相当于Shiro进行认证和授权的数据源,充当了Shiro与安全数据之间的“桥梁”或者"连接器”。也就是说,当对用户进行认证(登录)和授权(访问控制)验证时,Shiro会用应用配置的Realm中查找用户及其权限信息,即如果用户信息在Realm中不存在,则认证,授权失败。

Shiro JavaSE基础开发

前期准备

导包

创建shiro配置文件

在resource目录下创建名为shiro.ini的文件在文件中完成用户、角色及权限的配置

基本使用

初始化

//1.创建安全管理器
DefaultSecurityManager securityManager = new DefaultSecurityManager();

/ /2.创建realm
IniRealm iniRealm = new IniRealm( resourcePath: "classpath:shiro.ini");

//3.将realm设置给安全管理器
securityManager.setRealm(iniRealm); 

//4.将ReaLm设置给Securityutils工具
SecurityUtils.setSecurityManager(securityManager);

/ /5.通过SecurityUtils工具类获取subject对象
Subject subject = SecurityUtils.getSubject();

认证流程

//【认证流程】
// a.将认证帐号和密码封装到token对象中
UsernamePasswordToken token = new UsernamePasswordToken(username, password);

//b.通过subject对象调用Login方法进行认证申请:
boolean b = false;
try{
	subject. login(token);
	b = true;
}catch(IncorrectCredentialsException e){
	b = false;
}
System.out.println(b?"登录成功":"登录失败");

鉴权流程

//【授权】
//判断是否有某个角色
System.out.println(subject.hasRole( "seller" ));

//判断是否有某个权限
boolean permitted = subject.isPermitted( "order-del" );System. out.println(permitted) ;

整体流程

1.通过subject.login(token)进行登录,就会将token包含的用户信息(帐号和密码)传递给SecurityManager

2.SecurityManager就会调用Anthenticator进行身份认证

3.Anthenticator把token传递给对应的Realm

4.Realm根据得到的token,调用doGetAuthenticationInfo方法进行认证(如果认证失败通过抛出异常提示认证器)

5-7将认证结果一层—层返回到subject(如果subject.login)

SpringBoot整合Shiro

创建springboot应用

添加依赖

添加mybatis,druid依赖

整合Shiro

配置application.yml

创建配置文件

spirngboot不提供shiro的配置,所以要自己配

@Configuration
public class ShrioConfig {
    @Bean
    public IniRealm getIniRealm() {
        IniRealm iniRealm = new IniRealm("classpath:shiro.ini");

        return iniRealm;
    }

    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(IniRealm iniRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(iniRealm);

        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();

        filter.setSecurityManager(securityManager);

        //设置shiro的拦截规则
        // anon    匿名用户可访问
        // authc   认证用户可访问
        // user    使用RemeberMe的用户可访问
        // perms   对应权限可访问
        // role    对应的角色可访问
        Map<String, String> filterMap = new HashMap<>();
        filterMap.put(" /", "anon");
        filterMap.put(" /1ogin.htm1", "anon");
        filterMap.put(" /regist.htm1", "anon");
        filterMap.put(" /user/login", "anon");
        filterMap.put("/ user/regist", "anon");
        filterMap.put(" /static/**", "anon");
        filterMap.put("/**", "authc");

        filter.setFilterChainDefinitionMap(filterMap);

        filter.setFilterChainDefinitionMap(filterMap);
        filter.setLoginUrl("/1ogin.htm1");
        //设置未授权访问的页面路径,即找未授权时跳转的页面
        filter.setUnauthorizedUrl("/1ogin.htm1");


        return filter;
    }
}

登出

在Shiro过滤器中进行配置,配置logut对应的路径:filterMap. put( " /exit" , " logout" );

在页面的“退出”按钮上,跳转到logout对应的url:<a href= "exit">退出</ a>

认证测试

主要代码:

Service

@Service
public class CheckServiceImpl  {
    public void checkLogin(String userName,String userPwd) throws Exception{
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd) ;
        subject.login(token);
    }
}

Controller

@Resource
    private CheckServiceImpl userService;

    @RequestMapping("1ogin")
    public String login(String userName, String userPwd) {
        try {
            userService.checkLogin(userName, userPwd);
            System.out.println("------登录成功!");
            return "index";
        } catch (Exception e) {
            System.out.println("------登录失败!");
            return "login";
        }
    }

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title></head>
<body>
login
<hr/>
<form action="user/login">
    <p>帐号:<input type="text" name="userName "/></p>
    <p>密码:<input type="text" name="userPwd "/></p>
    <p><input type="submit" value="登录"/></p>
</form>
</body>
</html>

JDBCRealm

为什么要用?

前面的例子的realm(从shiro.ini加载)都是写死的,现在让Shiro从数据库中读取。

怎么用?

如果使用JdbcRealm,则必须提供JdbcRealm所需的表结构(权限设计)

JdbcRealm规定的表结构(必须遵守,否者报错)。

  • 用户信息表:users
create table users(
     id int primary key auto_increment,
     username varchar ( 68) not null,
     password varchar ( 20) not null,
     ...(按需添加)
);
  • 角色信息表:user_roles

create table user_roles(
       id int primary key auto_increment,
       username varchar ( 60) not null,
       role_name varchar ( 100) not null
       ...
};
  • 权限信息表:roles_permission

create table roles_permissions(
       id int primary key auto_increment,
       role_name varchar( 188) not null,
       perssion varchar ( 100) not null,
       ...
);

例子

设置数据源

//dataSource由配置好的Druid提供
@Bean
public JdbcRealm getJdbcRealm( DataSource dataSource){
	JdbcRealm jdbcRealm = new JdbcRealm();

	/ / JdbcRealm会自行从数据库查询用户及权限数据(数据库的表结构要符合	JdbcRealm的规范)
	jdbcRealm .setDataSource( dataSource) ;

	// JdbcRealn黑t认开启认证功能,需要手动开启授权功能
	jdbcRealm.setPermissionsLookupEnabled(true) ;

	return jdbcRealm;
}

认证测试,同上一章

Shiro的标签使用

当用户认证进入到主页面之后,需要显示用户信息及当前用户的权限信息;Shiro就提供了一套标签用于在页面来进行权限数据的呈现

JSP页面中引用

<%@taglib prefix="shiro" uri="http: / /shiro.apache.org/tags"%>

Thymeleaf模版中引用

在pom.xml文件中导入thymeleaf模版对shiro标签支持的依赖

<dependency>
	<groupId>com.github.theborakompanioni</groupId>
	<artifactId>thymeleaf-extras-shiro</artifactId>
	<version>2.8.0</version>
</dependency>

在ShiroConfig中配置相关依赖注入

@configuration
public class shiroConfig {
	@Bean
	public shiroDialect getshiroDialect(){
		return new shiroDialect();
	}

	……
}

Thymeleaf模版中引入shiro的命名空间

<html xmlns:th="hLtp : / / www.thymeleaf.org"
xmlns :shiro="http:/ / www .pollix.at/thymeleaf/shiro">
</html>

其它略

自定义Realm

为什么要用?

可以从上节看到jdbcReal对表的结构存在限制,不够灵活

认证流程回顾

  • subject调用login方法,将包含用户和密码的token传递给SecurityManager
  • SecurityManager就会调用认证器(Authenticator)进行认证
  • Authenticator就将token传递给绑定的Realm,在Realm中进行用户的认证检查;如果认证通过则正常执行,如果认证不通过则抛出认证异常

实验环境

表结构:

自定义过程

整体结构

使用jdbcRealm时需要使用固定结构的表,而使用自定义Realm时需要将自定义的表数据转换为Shiro权限验证需要的数据

  • Shiro进行认证需要用户信息,根据用户名查询用户信息

根据用户名查询当前用户的角色列表

根据用户名查询当前用户的权限列表

  • Shiro进行授权管理需要当前用户的角色和权限

重写认证

重写授权

principalcollection中的内容为认证函数返回的的info对象中的第一个参数。

myRealm注入

加密

基础知识

加密规则可以自定义,在项目开发中我们通常使用BASE64和MD5编码方式。

  • BASE64:可反编码的编码方式
    • 明文---密文

    • 密文----明文

  • MD5:不可逆的编码方式(非对称)
    • 明文----密文

加密过程

加密并不绝对安全,因为只要有有充足的时间与算力,再复杂的md5加密方式也会有被穷举法穷举出来的时候,当然如果付出大于回报,也没人这么干

过程1

前端明文传输,在业务层进行加密,加密后保存在数据库.

缺点:如果获取加密规则,则可以通过解密获取密码

过程2

使用md5进行加密,优点是无法通过算法解密获取密码。

缺点:可以建立密码-密文数据库(穷举所有合法的密码,通过md5规则加密,存入数据库以便查询),通过查询密文来获取密码,如果密码较为复杂,则较为安全,但是如果算力足够,或者破解时间久,也会有被解密的一天。

过程3

加盐:即在密码加密前拼接随机字符串(盐),然后再进行md5加密,这样做的好处就是即使被解密,得到的也是密码与盐的混合体,无法获取真实密码,还可以通过多层md5加密,增加破解难度。

如果数据库用户的密码存储的密文,Shiro该如何完成验证呢?

使用Shiro提供的加密功能,对输入的密码进行加密之后再进行认证,加密次数要与组注册时的加密次数一致

加盐+加密*n,当然,需要把盐存入数据库

修改myRealm,返回认证信息时需要同时返回加盐

授权

用户登录成功之后,要进行响应的操作就需要有对应的权限;在进行操作之前对权限进行检查―授权权限控制通常有两类做法:

  • 不同身份的用户登录,我们显示不同的操作菜单(没有权限的菜单不显示)
  • 对所有用户显示所有菜单,当用户点击菜单以后再验证当前用户是否有此权限,如果没有则提示权限不足

HTML授权

在菜单页面只显示当前用户拥有权限操作的菜单. shiro标签

<shiro:hasPermission name= "sys:c :save">
<dd><a href=" javascript: ; ">入库</ a></ dd>< / shiro:hasPermission>

过滤器授权

在shiro过滤器中对请求的url进行权限设置

filterMap . put( " /c_add . html " , " perms[ sys:c :save] " );
//设置未授权访问的页面路径-当权限不足时显示此页面
filter . setUnauthorizedUr1( " / lesspermission. html" );

注解授权

配置spring对shiro注解的支持

再请求的控制器添加注解

通过全局异常处理,指定权限不足时的页面跳转

基于代码的校验

在代码中进行手动权限校验

缓存使用

为什么要用?

使用Shiro进行权限管理过程中,每次授权都会访问realm中的doGetAuthorizationInfo方法查询当前用户的角色及权限信息,如果系统的用户量比较大则会对数据库造成比较大的压力。

Shiro支持缓存以降低对数据库的访问压力(缓存的是授权信息)

怎么用?

导入缓存依赖(使用ehcahe做缓存)

缓存配置文件

加入缓存管理

Session管理

为什么要用?

Shiro进行认证和授权是基于session实现的,Shiro包含了对session的管理。在某些场景中,需要对session进行配置,如银行的页面超时时需要设置很短

怎么用?

如果我们需要对session进行管理,首先需要,自定义session管理器,将自定义的session管理器设置给SecurityManager

配置sessionManager

rememberMe

记住密码

为什么要用?

对于一些展示性页面,当用户选择了记住我,即使好久都没有认证,也可以正常访问,但是如果想要修改,则需要重新登录

Shiro将用户对页面访问的权限分为三个级别:

  • 未认证―可访问的页面-(陌生人)—问候
  • 记住我―可访问的页面-(前女友)-朋友间的拥抱
  • 已认证-可访问的页面-(现女友)-牵手

流程

怎么用?

指定哪些页面为记住我下可以访问。

设置Cookie管理器并进行配置

在token中写入记住我

多Realm

为什么要用?

当shiro进行权限管理,数据来自于不同的数据源时,我们可以给SecurityManager配置多个Realm,或者在前端选择用户登录或管理员登录后,可以选择使用指定的Realm

怎么用?

多Realm处理方式

链式处理:多个Realm依次进行认证

分支处理:根据不同的条件从多个Realm中选择—个进行认证处理

认证源码

链式处理

两个Realm

在ShiroConfig.java中为SecurityManager配置多个Realm

分支处理

原理

将选择Realm的参数写入自定义的Token,然后通过继承认证器重写其中的doAuthentication方法,当Shiro进行认证时,就会调用自定义的doAuthentication方法,从而完成自定义Realm选择

流程

自定义token

自定义认证器

配置自定义认证器

posted @ 2022-06-07 21:27  EA2218764AB  阅读(343)  评论(0编辑  收藏  举报