shiro实战教程

一、权限管理

1.1什么是权限管理

基本上涉及到用户参与的系统都需要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。

权限管理包括用户身份认证和授权两部分,简称认证授权。对于需要访问控制的资源,用户首先经过身份认证通过后,如果该用户有访问这个资源的权限才可以继续访问。

1.2用户身份认证

概念

所谓的身份认真就是判断一个用户是否是合法用户的过程。最常见的就是通过用户输入用户名和密码来校验,看其是否和系统中存储的用户名和密码一致,以此来判断用户的身份是否合法。对于门禁刷卡系统,需要刷卡,对于指纹登录认证,则填充指纹。

身份认证流程

Subject(主体)

访问系统的用户,其实需要认证的都可被称为主体。

Principal(身份信息)

主体的标识,表示具有唯一性,例如你的身份证号码,手机号。一个主体可以有多个角色但是必须有一个主身份。

credentials(凭证信息)

例如密码、证书这些。凭证信息只有主体自己知道。

1.3用户授权

授权又称为访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源是没有权限进行访问的。

授权流程

授权可以理解为主体认证后继续判断是否具有对本资源访问的权限,如果有的话,可以进行访问,如果没有,则拒绝访问

上面提到的有三个关键对象:主体(Subject)、资源(Resource)、权限/许可(Permission);

权限又分为粗粒度和细粒度,粗粒度权限是指对资源类型的权限,细粒度权限是对资源实例的权限。

粗粒度权限管理:用户具有用户管理的权限,具有导出订单明细的权限。对资源实例的控制称为细粒度权限控制,就是控制数据级别的权限。比如:用户只被允许修改本部门的员工信息,用户只允许导出自己创建的订单。

权限分配

对主体分配权限,主体只允许在权限范围内对资源进行操作。例如用户user1分配了对商品信息修改的权限,所以user1只能对商品进行信息修改。

权限分配的数据通常需要持久化,可以将上面的数据模型创建边并将用户具有的权限信息存储到数据库中。

授权方式

  • 基于角色的访问控制

    • 基于角色的访问控制是以角色为中心进行控制,例如:判断用户A是否具有admin权限

      if(userA.hasRole("admin")){
      // 有权限
      }else{
      // 无权限
      }
      // 判断A是否有多个角色
      userA.hasAllRoles(Arrays.asList("role1","role2"));
  • 基于资源的访问控制

    • 以资源为中心进行访问

      例如用户A对用户管理模块中的所有资源有修改用户信息的权限。

      if(A.isPermission("user:update:*")){
      // A可以修改user信息,如果换成“uer:update:u1”;就表示只能对u1的用户信息进行修改
      }

      这里需要知道权限字符串上面代码中的"user:update:*"就是权限字符串,它的格式是:资源标识符:操作:资源实例标识符,意思是对哪个资源的哪个实例具有哪些操作的权限,中间是以:作为分隔符,*代表通配符,表示所有。

      例子:

      • 用户创建的权限:user:create:*,或者user:create
      • 用户对实例001的所有权限:user:*:001

二、shiro框架

什么是shiro?

Apache Shiro is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.

Apache Shiro是一个功能强大且易于使用的Java安全框架,它执行身份验证,授权,加密和会话管理。使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大的Web和企业应用程序。

http://shiro.apache.org/

Shiro是Apache下的一个开源框架,他将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。

核心架构图

  • Subject

    主体,外部应用与Subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。subject在Shiro中是一个接口,接口中定义了很多认证授权的方法,外部程序通过subject进行认证授权,而subject是通过SecurityManager进行认证授权的。

  • SecurityManager

    安全管理器,对全部的subject进行安全管理,他是shiro的核心,也是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。

  • Authenticator

    对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。

  • Authorizer

    用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限.

  • SessionManager

    会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

  • SessionDAO

    SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库.

  • realm

    Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。

  • CacheManager

    即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。

  • Cryptography

    即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能.

认证测试demo

public class TestAuthenticator {
public static void main(String[] args) {
// 创建安全管理对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 设置realm
securityManager.setRealm(new IniRealm("classpath:shiro-authenticator.ini"));
// 安全工具类设置安全管理对象
SecurityUtils.setSecurityManager(securityManager);
// 创建一个主体
Subject subject = SecurityUtils.getSubject();
// 生成Token
UsernamePasswordToken token = new UsernamePasswordToken("xiaohei","123"); try {
// 开始认证
subject.login(token);
System.out.println("认证状态: "+subject.isAuthenticated()); //true
}catch (Exception e){
e.printStackTrace();
}
} }

认证最终执行用户名比较的是SimpleAccountRealm类中的 doGetAuthenticationInfo在这个方法中完成用户校验。

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken)token;
SimpleAccount account = this.getUser(upToken.getUsername());
if (account != null) {
if (account.isLocked()) {
throw new LockedAccountException("Account [" + account + "] is locked.");
} if (account.isCredentialsExpired()) {
String msg = "The credentials for account [" + account + "] are expired";
throw new ExpiredCredentialsException(msg);
}
} return account;
}

2、最终密码校验是在AuthenticatingRealm类的``assertCredentialsMatch`方法。

protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
CredentialsMatcher cm = this.getCredentialsMatcher();
if (cm != null) {
if (!cm.doCredentialsMatch(token, info)) {
String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
throw new IncorrectCredentialsException(msg);
}
} else {
throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify credentials during authentication. If you do not wish for credentials to be examined, you can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
}
}

cm.doCredentialsMatch(token, info)方法校验参数的token和从ini文件或者数据库中取出的认证信息是否一致,这个方法是CredentialsMatcher接口的唯一方法,用来验证token和认证信息是否匹配。

实现此方法的类有四个,这里debug走的是SimpleCredentialsMatcher类实现的方法

public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
Object tokenCredentials = this.getCredentials(token);
Object accountCredentials = this.getCredentials(info);
return this.equals(tokenCredentials, accountCredentials);
}

直接强转为Object,然后通过equals方法进行比较。

自定义Realm

自定义Realm,只需要继承AuthorizingRealm抽象类。实现对应的认证(doGetAuthenticationInfo)和授权方法(doGetAuthorizationInfo)。因为AuthorizingRealm已经实现了Authorizer、PermissionResolverAware接口,继承了AuthenticatingRealm抽象类。

图中虚线是接口的实现,蓝色实现代表类的继承。

shiro授权

授权流程

授权方式

编程式

Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("admin")){
//有权限
}else{
//无权限
}

注解式

@RequiresRoles("admin")
public void deleteUserById(Integer id){
// 如果有admin的权限才会成功调用这个方法来删除用户
}

标签式

JSP页面中通过以下标签完成授权
<shiro:hasRole name="admin">
<!--有权限-->
</shiro:hasRole>
Thyemeleaf需要额外的配置

授权测试

  • 基于角色的访问控制

    package club.qy.hu;
    
    import club.qy.hu.realm.MD5CustormRealm;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.subject.Subject; import java.util.Arrays; public class TestCustomRealm {
    public static void main(String[] args) {
    // 创建安全管理对象
    DefaultSecurityManager securityManager = new DefaultSecurityManager();
    //创建自定义Realm
    MD5CustormRealm md5CustormRealm = new MD5CustormRealm();
    //创建 HashedCredentialsMatcher对象
    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    //设置算法名称和散列次数
    credentialsMatcher.setHashAlgorithmName("md5");
    credentialsMatcher.setHashIterations(1024); md5CustormRealm.setCredentialsMatcher(credentialsMatcher); // 设置成自定义认证
    securityManager.setRealm(md5CustormRealm); // 安全工具类设置安全管理对象
    SecurityUtils.setSecurityManager(securityManager);
    // 创建一个主体
    Subject subject = SecurityUtils.getSubject();
    // 生成Token
    UsernamePasswordToken token = new UsernamePasswordToken("xiaozhang", "123"); try {
    // 开始认证
    subject.login(token);
    System.out.println("认证状态: " + subject.isAuthenticated());
    } catch (Exception e) {
    e.printStackTrace();
    }
    if (subject.isAuthenticated()) {
    //如果认证成功,进行权限判断
    System.out.println("----------基于角色的权限认证------------");
    if (subject.hasRole("admin")) {
    System.out.println("该用户具有admin角色的权限");
    }
    if (subject.hasAllRoles(Arrays.asList("admin", "super"))) {
    System.out.println("该用户具有admin,super 角色的权限");
    } } }
    }

    自定义realm

    package club.qy.hu.realm;
    
    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 org.apache.shiro.util.ByteSource; import java.util.Arrays; public class CustormRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String principal = (String) principalCollection.getPrimaryPrincipal();
    System.out.println("认证信息:"+principal);
    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
    // 添加角色
    simpleAuthorizationInfo.addRole("admin");
    simpleAuthorizationInfo.addRole("super"); return simpleAuthorizationInfo;
    } @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    String principal = (String) authenticationToken.getPrincipal();
    // 查询数据库得到 username
    String username = "xiaozhang";
    String salt = "!w46H>,";
    if (username.equals(principal)){
    // 第一个参数是用户名也叫做认证信息,
    // 第2个参数代表从数据库中取出的加密的密码,
    // 第3个参数代表加密所用的盐,
    // 第4个参数是SimpleAuthenticationInfo的名字
    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(principal,
    "fd6b93fd376e3d9aa012f33eb58641b0", ByteSource.Util.bytes(salt),this.getName());
    return authenticationInfo;
    } return null;
    }
    }

    输出结果

  • 基于资源的访问控制

    自定义Realm

    public class CustormRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String principal = (String) principalCollection.getPrimaryPrincipal();
    System.out.println("认证信息:"+principal);
    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); // 添加权限字符串
    simpleAuthorizationInfo.addStringPermission("user:create");
    simpleAuthorizationInfo.addStringPermissions(Arrays.asList("user:*:01","user:update:*"));
    return simpleAuthorizationInfo;
    } @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    String principal = (String) authenticationToken.getPrincipal();
    // 查询数据库得到 username
    String username = "xiaozhang";
    String salt = "!w46H>,";
    if (username.equals(principal)){
    // 第一个参数是用户名也叫做认证信息,
    // 第2个参数代表从数据库中取出的加密的密码,
    // 第3个参数代表加密所用的盐,
    // 第4个参数是SimpleAuthenticationInfo的名字
    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(principal,
    "fd6b93fd376e3d9aa012f33eb58641b0", ByteSource.Util.bytes(salt),this.getName());
    return authenticationInfo;
    } return null;
    }
    }

    测试类

    public static void main(String[] args) {
    // 创建安全管理对象
    DefaultSecurityManager securityManager = new DefaultSecurityManager();
    //创建自定义Realm
    MD5CustormRealm md5CustormRealm = new MD5CustormRealm();
    //创建 HashedCredentialsMatcher对象
    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    //设置算法名称和散列次数
    credentialsMatcher.setHashAlgorithmName("md5");
    credentialsMatcher.setHashIterations(1024); md5CustormRealm.setCredentialsMatcher(credentialsMatcher); // 设置成自定义认证
    securityManager.setRealm(md5CustormRealm); // 安全工具类设置安全管理对象
    SecurityUtils.setSecurityManager(securityManager);
    // 创建一个主体
    Subject subject = SecurityUtils.getSubject();
    // 生成Token
    UsernamePasswordToken token = new UsernamePasswordToken("xiaozhang", "123"); try {
    // 开始认证
    subject.login(token);
    System.out.println("认证状态: " + subject.isAuthenticated());
    } catch (Exception e) {
    e.printStackTrace();
    }
    if (subject.isAuthenticated()) {
    //如果认证成功,进行权限判断 System.out.println("----------基于资源的权限认证------------");
    System.out.println(subject.isPermitted("user:create"));
    System.out.println(subject.isPermitted("user:update:01")); //分别具有哪些权限
    for (boolean b : subject.isPermitted("user:delete", "user:*:01")) {
    System.out.println(b);
    } // 判断是否同时具有指定权限
    System.out.println(subject.isPermittedAll("user:delete", "user:*:01"));
    } }

    输出结果

Shiro的认证与授权的更多相关文章

  1. 使用Shiro实现认证和授权(基于SpringBoot)

    Apache Shiro是一个功能强大且易于使用的Java安全框架,它为开发人员提供了一种直观,全面的身份验证,授权,加密和会话管理解决方案.下面是在SpringBoot中使用Shiro进行认证和授权 ...

  2. Shiro入门之一 -------- Shiro权限认证与授权

    一  将Shirojar包导入web项目 二 在web.xml中配置shiro代理过滤器 注意: 该过滤器需要配置在struts2过滤器之前 <!-- 配置Shiro的代理过滤器 -->  ...

  3. shiro权限认证与授权

    什么是shiro? Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权.加密.会话管理等功能,组成了一个通用的安全认证框架. 为什么要用sh ...

  4. ssm整合shiro—实现认证和授权

    1.简述 1.1    Apache Shiro是Java的一个安全框架.是一个相对简单的框架,主要功能有认证.授权.加密.会话管理.与Web集成.缓存等. 1.2   Shiro不会去维护用户.维护 ...

  5. Spring Boot 整合 Shiro实现认证及授权管理

    Spring Boot Shiro 本示例要内容 基于RBAC,授权.认证 加密.解密 统一异常处理 redis session支持 介绍 Apache Shiro 是一个功能强大且易于使用的Java ...

  6. 在web项目中使用shiro(认证、授权)

    一.在web项目中实现认证 第一步,在web项目中导入shiro依赖的包 第二步,在web.xml中声明shiro拦截权限的过滤器 <filter> <filter-name> ...

  7. 详解登录认证及授权--Shiro系列(一)

    Apache Shiro 是一个强大而灵活的开源安全框架,它干净利落地处理身份认证,授权,企业会话管理和加密.Apache Shiro 的首要目标是易于使用和理解.安全有时候是很复杂的,甚至是痛苦的, ...

  8. Vert.x(vertx) 认证和授权

    每个线上系统几乎都是离不开认证和授权的,Vert.x提供了灵活.简单.便捷的认证和授权的支持.Vert.x抽象出了两个核心的认证和授权的接口,一个是 AuthProvider,另一个是User.通过这 ...

  9. 用户登录安全框架shiro—用户的认证和授权(一)

     ssm整合shiro框架,对用户的登录操作进行认证和授权,目的很纯粹就是为了增加系统的安全线,至少不要输在门槛上嘛. 这几天在公司独立开发一个供公司内部人员使用的小管理系统,客户不多但是登录一直都是 ...

随机推荐

  1. 三、Zookeeper简介

    一.简介 zookeeper 主要使用场景:分布式系统的分布式协同服务.协同工作就是通过某种方式,让着节点的信息能够同步和共享,依赖于进程间的通信.通信方式有俩种. 通过网络进行信息共享 现实工作中, ...

  2. redis学习之——五大基本数据类型

    redis 键 (key) 基本数据类型:string 字符串 list (列表)  set(集合)  hash(类似java 中的Map)   zset(有序集合) 官方命令doc redis 键 ...

  3. linux文件实时同步

    参考博客:https://www.cnblogs.com/MacoLee/p/5633650.html 一.文件同步很简单 服务端:被动的接收传输过来的数据 客户端:主动提供数据给服务端 安装思路:服 ...

  4. 在Qt中配置海康工业相机SDK及遇到的问题(报错)

    1.在项目的.pro文件里导入海康工业相机的SDK路径 INCLUDEPATH += \ D:\HKVersion\MVS_3.1.0\MVS\Development\Includes #这时到入Op ...

  5. 在线CC攻击网站源码

    源码目录 index.html 首页 cc.php 核心文件 count.php 使用统计 pv.php 访问测试页面 ip.txt 代理IP数据文件 运行方式 域名/?url=目标网址 要先获取代{ ...

  6. 使用MySQL Shell创建MGR

    本篇知识点: 配置MGR所需的参数 使用MySQL Shell配置MGR shell.connect() var 设定临时变量 dba.createCluster() dba.getCluster() ...

  7. Kubernetes 最佳安全实践指南

    原文链接:https://fuckcloudnative.io/posts/security-best-practices-for-kubernetes-pods/ 对于大部分 Kubernetes ...

  8. 浏览器开发者工具network详解

    General概诉 请求链接 Request URL: 请求方式 Request Method: 代码状态 Status Code: 远程地址 Remote Address: 引用协议 用于过滤 Re ...

  9. 微信小程序api拦截器

    微信小程序api拦截器 完美兼容原生小程序项目 完美兼用小程序api的原本调用方式,无痛迁移 小程序api全Promise化 和axios一样的请求方式 小程序api自定义拦截调用参数和返回结果 强大 ...

  10. Leetcode——练习

    平时没事刷刷Leetcode,还办了个年会员.为了自己150刀.为了自己的大脑投资,从不差钱儿.刷刷题能练习coding,此外看一些别人的优秀的答案,能增长见解.大家共同努力,共勉. 十.Google ...