【权限管理框架】一文看懂Shiro权限管理框架!1

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 【权限管理框架】一文看懂Shiro权限管理框架!

1.JavaWeb中的权限控制

(1)什么是权限控制

  • 忽略特别细的概念,比如权限能细分很多种,功能权限,数据权限,管理权限等
  • 理解两个概念:用户和资源,让指定的用户,只能操作指定的资源(CRUD)

(2)javaweb中怎么处理

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 访问控制列表
  • 以前盛行的一种权限设计,它的核心在于用户直接和权限挂钩
  • 优点:简单易用、开发便捷
  • 缺点:用户和权限直接挂钩,导致在授权时的复杂性,比较分散,不便于管理
  • 案例:常见的文件系统权限设计,直接给用户加权,类似Linux系统中的chmod



8d7388acbb77490fa7f46447a160449f.jpg

RBAC:Role Based Access Control

  • 基于角色的访问控制系统。权限与角色相关联,用户通过适当的角色的成员而获得角色的权限
  • 优点:简化了用户与权限的管理,
  • 通过对用户进行分类,使得角色与权限关联起来
  • 缺点:开发相比ACL复杂
  • 案例:基于RBAC模型的权限验证框架有Apache Shiro、Spring Security



0f2e9609a6a845c4b683219fbb16e897.jpg

  • 总结:权限设计不能太过于复杂,否则性能会下降

2.2主流权限框架介绍

(1)什么是Spring Security

Spring Security是一个能够基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI和AOP功能,为应用系统提供声明式的安全访问控制功能,减少了企业系统安全控制编写大量重复代码的工作。

(2)什么是Apache Shiro

Apache Shiro是一个强大且易用的java安全框架,执行身份验证、授权、密码和会话管理。使用shiro的易于理解的API,可以快速、轻松的执行任何应用程序。

两个的优缺点

  • Apache Shiro比Spring Security使用更简单
  • Shiro功能强大、简洁、灵活,不跟任何的框架或者容器绑定,可以独立运行
  • SpringSecurity对Spring体系支持比较友好,脱离Spring体系开发很难
  • SpringSecuity支持Oauth鉴权,Shiro需要自己实现

3.Shiro架构和基本概念

3.1.Shiro的4大核心模块

(1)Shiro的四大核心模块分为身份认证、授权、会话管理和加密

  • 身份认证
  • Authentication,身份认证,一般就是登录
  • 授权
  • Authorization,给用户分配角色或者访问某些资源的权限
  • 会话管理
  • Session Management,用户的会话管理员,多数情况下是web Session
  • 加密
  • Cryptogarphy,数据加解密,你如密码加解密等


(2)Shiro架构图

35511df34bef405b8801fb193c85cf93.jpg

3.2.Shiro权限控制运行流程

(1)Shiro常见名称

  • Subject
  • 我们把用户或者程序称为主体,主体去访问资源或者系统
  • SecurityManager
  • 安全管理器,Subject的认证和授权都在安全管理器下进行
  • Authenticator
  • 认证器,主要负责Subject的认证
  • Realm
  • 数据域,Shiro和安全数据的连接器,好比jdbc连接数据库;通过realm获取认证授权的相关信息
  • Authorizer
  • 授权器,主要负责Subject的授权,控制subject拥有的角色或者权限
  • Cryptography
  • 加解密,Shiro包含易于使用和理解的数据加密方法,简化了很多复杂的API
  • CacheManager
  • 缓存管理器,比如认证或者授权信息,通过缓存进行管理,提高性能
  • SessionManager
  • 会话管理器,大多数是web session
  • SessionDAO
  • SessionDAO即会话,是对session会话的一套接口,比如要将session存储到数据库。

4.Shiro简单API案例

4.1.项目搭建所需依赖

  • 环境准备:maven3.5+jdk8+springboot+idea
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql starter 注意一定要把runtime去掉-->  
<dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--测试模块starter-->  
<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.1.6</version>
</dependency>
<!--shiro相关依赖包-->    
<dependency>
     <groupId>org.apache.shiro</groupId>
     <artifactId>shiro-spring</artifactId>
     <version>1.4.0</version>
</dependency>

4.2.Shiro认证简单实操

(1)Shiro的认证流程

  • 创建Security Manager:Security Manager是用来提供安全服务的,所以在做shiro认证的时候要先创建此对象
  • 主题Subject提交请求给Security Manager
  • Security Manager调用Authenticator组件做认证
  • Authenticator通过Realm来从数据源中获取认证数据


fc6ab3dfcdc443b2acb2b4107d472e13.jpg

(2)编码测试

@SpringBootTest
public class Test{
    //声明SecurityManager
    DefaultSecurityManager securityManager = new DefaultSecurityManager();
    //声明Realm
    SimpleAccountRealm accountRealm = new SimpleAccountRealm();
    @BeforeTest
    public void init(){
        accountRealm.addAccount("lixiang","123456");
        accountRealm.addAccount("lisi","123456");
        //构建环境
        securityManager.setRealm(accountRealm);
    }
    @Test
    public void test(){
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernameAndPasswordToken token = new UsernameAndPasswordToken("lixiang","123456");
        subject.login(token);
        System.out.println("认证结果:"+subject.isAuthenticated());
    }
}

(3)测试结果

8e685725b3a945ffb66657fd3f18213a.jpg

b0a06d8f352744b6a356e70e7bcaae2b.jpg

4.3.Shiro授权简单实操

(1)常用API

//是否有对应角色
subject.hasRole("root")
//获取subject名
subject.getPrincipal()
//检查是否有对应的角色,无返回值,直接在SecurityManager里面进行判断
subject.checkRole("admin")
//检查是否有对应的角色
subject.hasRole("admin")
//退出登录
subject.logout();

(2)编码实操

 @Test
    void contextLoads() {
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("lixiang","123456");
        subject.login(usernamePasswordToken);
        System.out.println("认证结果:"+subject.isAuthenticated());
        System.out.println("获取subject主体的唯一标识:"+subject.getPrincipal());
        //检查是否有对应角色,无返回值,直接在SecurityManager里面进行判断
        subject.checkRole("admin");
        //检查是否有对应的角色
        System.out.println("是否有对应角色:"+subject.hasRole("admin"));
        //退出登录
        subject.logout();
        System.out.println("认证结果:"+subject.isAuthenticated());
    }

745bc772acd94e5b841eb3f617f2328c.jpg


10845071c58d4f68a2b459d6dc552059.jpg

5.安全数据来源Realm

5.1.Realm简介和继承关系

  • Realm的作用:Shiro从Realm中获取安全的数据
  • Realm中的两个概念:
  • principal:主体的标识,可以有多个,但是必须要有一个唯一性的,常见的用户名、手机号、邮箱
  • credential:访问凭证,一般就是密码
  • 如果要自定义Realm,继承AuthorizingRealm


391704f204634f8ca12c8c79c40e14ca.jpg

  • Realm:顶级接口,所有类的父接口
  • CachingRelam:带有缓存功能的Realm抽象类
  • AuthenticatingRealm:带有认证功能的Realm抽象类
  • AuthorizingRealm:带有授权功能的Realm抽象类
  • SimpleAccountRealm:提供一些简单的Realm认证
  • TextConfigurationRealm:提供文本形式的Realm认证
  • IniRealm和PropertiesRealm:TextConfigurationRealm的子类,细化文本验证方式
  • JdbcRealm:与数据库交互的Realm认证
  • DefaultLdapRealm:根据LDAP进行身份验证

5.2.Shiro内置IniRealm权限验证

(1)新建shiro.ini文本文件,编写规则

#用户模块,对应用户名、密码、角色,多个角色之间用逗号隔开
[users]
lixiang = 123456,user
zhangsan = 123456,admin,root
#权限模块,对应角色名称、对应权限,多个权限用,分隔
[roles]
user = video:find,video:buy
admin = video:*
root = *

c86fc574a94743718a31cfb856362e59.jpg

(2)测试编码

@Test
public void test(){
    //创建IniSecurityManagerFactory工厂实例,注意这块一定要是shiro下的包
    //IniSecurityManagerFactory这个类已经废弃了,这里只做验证
    Factory<SecurityManager> factory = new IniSecurityManagerFactory();
    //获取工厂实例
    SecurityManager securityManager = factory.getInstance();
    //将securityManager设置到当前运行环境当中
    SecurityUtils.setSecurityManager(securityManager);
    //获取Subject对象
    Subject subject = SecurityUtils.getSubject();
    //创建登录Token
    UsernameAndPasswordToken token = new UsernameAndPasswordToken("lixiang","123456");
    //验证
    subject.login(token);
    //判断是否有对应角色
    System.out.print("判断是否有对应角色:"+subject.hasRole("admin"));
    //判断是否有对应的权限
    System.out.print("判断是否有对应权限:"+subject.isPermitted("video:find"));
    //判断是否有对应的权限,无返回值,如果检验不通过则抛出异常
    //checkPermission("find:video")
}

de0c51ff8cf040d2a36185174e086412.jpg

5.3.Shiro内置JdbcRealm权限验证

(1)配置jdbcrealm.ini文件,注意这块一定要是ANSI格式否则运行会抛错

#注意 文件格式必须为ini,编码为ANSI
#声明Realm,指定realm类型
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
#配置数据源
#dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
dataSource=com.alibaba.druid.pool.DruidDataSource
# mysql-connector-java 5 用的驱动url是com.mysql.jdbc.Driver,mysql-connector-java6以后用的是com.mysql.cj.jdbc.Driver
dataSource.driverClassName=com.mysql.cj.jdbc.Driver
#避免安全警告
dataSource.url=jdbc:mysql://120.76.62.13:3606/xdclass_shiro?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
dataSource.username=test
dataSource.password=Xdclasstest
#指定数据源
jdbcRealm.dataSource=$dataSource
#开启查找权限, 默认是false,不会去查找角色对应的权限,坑!!!!!
jdbcRealm.permissionsLookupEnabled=true
#指定SecurityManager的Realms实现,设置realms,可以有多个,用逗号隔开
securityManager.realms=$jdbcRealm
  • 如果编码不是ANSI格式

38a0cd2419c243c7ace52f4c6641a7fd.jpg

(2)验证

配置文件中 jdbcRealm.permissionsLookupEnabled=true 一定要设置成true,默认是false不会去校验角色

 @Test
    void contextLoads() {
        //创建SecurityManager工厂
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:jdbcrealm.ini");
        //拿到工厂
        SecurityManager securityManager = factory.getInstance();
        //将securityManager设置到当前运行环境当中
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("jack","123");
        subject.login(token);
        System.out.println("认证结果:"+subject.isAuthenticated());
        System.out.println("是否有对应的角色:"+subject.hasRole("user"));
        //查询是否有权限,无返回值,没有则抛异常
        //subject.checkPermission("video:delete");
        //查询是否有权限,有返回值
        System.out.println(subject.isPermitted("video:delete"));
    }


49f38778b79f47878f8a7df87b9a03e1.jpg

5.4.Shiro自定义Realm权限配置

(1)自定义Realm步骤

(1)创建一个类,继承AuthorizingRealm->AuthenticatingRealm->CachingRealm->Realm
(2)重写授权方法:doGetAuthorizationInfo(进行权限校验的时候会调用)
(3)重写认证方法:doGetAuthenticationInfo(当用户登陆的时候会调用)

(2)对象介绍

  • UsernamePasswordToken : 对应就是 shiro的token中有Principal和Credential
  • UsernameAndPasswordToken->HostAuthenticationToken->AuthenticationToken
  • SimpleAuthorizationInfo:代表用户角色权限信息
  • SimpleAuthenticationInfo:代表该用户的认证信息

(3)编写自定义的Realm类

public class CustomRealm extends AuthorizingRealm {
    //user
    private final static Map<String,String> userMaps = new HashMap<>();
    {
        userMaps.put("lixiang","123");
        userMaps.put("lisi","123");
    }
    //roles - > permission
    private final static Map<String,Set<String>> permissionMaps = new HashMap<>();
    {
        Set<String> set1 = new HashSet<>();
        Set<String> set2 = new HashSet<>();
        set1.add("video:find");
        set1.add("video:buy");
        set2.add("video:add");
        set2.add("video:delete");
        permissionMaps.put("lixiang",set1);
        permissionMaps.put("lisi",set2);
    }
    //user -> role
    private final Map<String,Set<String>> roleMap = new HashMap<>();
    {
        Set<String> set1 = new HashSet<>();
        Set<String> set2 = new HashSet<>();
        set1.add("role1");
        set1.add("role2");
        set2.add("root");
        roleMap.put("jack",set1);
        roleMap.put("xdclass",set2);
    }
    /**
     * 进行权限验证的时候调用
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("进行权限验证doGetAuthorizationInfo");
        String username = principals.getPrimaryPrincipal().toString();
        Set<String> permissions = getPermissionsfromDB(username);
        Set<String> roles = getRolesfromDB(username);
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setRoles(roles);
        simpleAuthorizationInfo.setStringPermissions(permissions);
        return simpleAuthorizationInfo;
    }
    /**
     * 通过用户名查找角色
     * @param username
     * @return
     */
    private Set<String> getRolesfromDB(String username) {
        return roleMap.get(username);
    }
    /**
     * 通过用户名查找权限
     * @param username
     * @return
     */
    private Set<String> getPermissionsfromDB(String username) {
        return permissionMaps.get(username);
    }
    /**
     * 进行身份验证的时候调用
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println(" 进行身份验证doGetAuthenticationInfo");
        String username = token.getPrincipal().toString();
        String pwd = getPwdfromDB(username);
        if("".equals(pwd) || pwd == null){
            return null;
        }
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,pwd,this.getName());
        return simpleAuthenticationInfo;
    }
    private String getPwdfromDB(String username) {
        return userMaps.get(username).toString();
    }
}

(4)测试

SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("lixiang","123");
//登录
subject.login(token);
//唯一标识
System.out.println("用户名:"+subject.getPrincipal());
System.out.println("是否有对应的角色:"+subject.hasRole("role1"));
System.out.println("是否有对应的权限:"+subject.isPermitted("video:find"));

2ad3d31d1ae54ae98b8b6a26ff6ceb82.jpg

5.5.Shiro源码认证授权流程

3c031a0071c442ecb12becfb768c41e1.jpg

认证流程:

  • subject.login(token)
  • DelegatingSubject.login(token)

AuthenticatingSecurityManager.authenticate(token)

AbstractAuthenticator.authenticate(token)

ModulearRealmAuthenticator.doAuthenticate(token)

ModulearRealmAuthenticator.doSingleRealmAuthentication(token)

AuthenticatingRealm.getAuthenticationInfo(token)




4c1eecd616e549359f28da169847ff5c.jpg

鉴权流程:

  • subject.checkRole(“admin”)
  • DelegatingSubject.checkRole()
  • AuthorizingSecurityManager.checkRole()
  • ModulatRealmAuthorizer.checkRole()
  • AuthorizingReaim,hasRole()
  • AuthorizingRealm.doGetAuthorizationInfo()











相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6月前
|
存储 安全 数据库
管理端开发如何快速理解并实现权限控制总结
管理端开发如何快速理解并实现权限控制总结
|
存储 前端开发 安全
权限管理与Shiro入门-1
权限管理与Shiro入门-1
172 1
|
存储 缓存 安全
权限管理与Shiro入门-2
权限管理与Shiro入门-2
77 2
|
NoSQL Java Redis
【权限管理框架】一文看懂Shiro权限管理框架!3
【权限管理框架】一文看懂Shiro权限管理框架!
|
存储 缓存 算法
【权限管理框架】一文看懂Shiro权限管理框架!2
【权限管理框架】一文看懂Shiro权限管理框架!
|
存储 数据库 数据安全/隐私保护
Shiro角色和权限管理
Shiro角色和权限管理
|
存储 缓存 前端开发
2021年你还不会Shiro?----8.使用Shiro实现权限管理(前后端)
这是一个系列的文章,这是第八篇,如果只是看了这一篇或者对于Shiro没有基础的人,看到这一篇可能并不会有多大收益。前面几篇文章已经介绍了Shiro+JSP+SpringBoot+Mybatis+mysql的整合,并实现了使用MD5+盐+hash散列的方式对密码进行加密的注册登录功能。这篇是基于之前的文章进行写作的,下面就要说下登录完成后怎么实现授权操作。也就是怎么使用Shiro实现权限管理,前后端的授权是分开的,准确的说是没有关系的,所以这里也是对前后端的授权操作分开讲解。
375 0
2021年你还不会Shiro?----8.使用Shiro实现权限管理(前后端)
|
开发框架 前端开发 安全
基于MVC框架实现权限控制简介
基于MVC框架实现权限控制简介
211 0
基于MVC框架实现权限控制简介
|
安全 NoSQL Java
权限管理-整合 SpringSecurity(2) | 学习笔记
快速学习 权限管理-整合 SpringSecurity(2)
108 0
权限管理-整合 SpringSecurity(2) | 学习笔记
|
前端开发 API Nacos
权限管理-前端整合 | 学习笔记
快速学习 权限管理-前端整合
权限管理-前端整合 | 学习笔记