前言

之前写项目用了 Shiro 框架,来进行安全验证以及权限管理。当时项目赶得急,没怎么深入了解,只能说能跑能改,不过在使用的过程中发现 Shiro 确实很优秀。现在回过头来学习原理,读读源码,深入的学习下。·

本篇博文主要写的是关于使用 Shiro 起步时最重要的一块,找了一些资料,力求写得简单明了。

简介

Realm:域,Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的 Realm 中查找用户及其权限信息。从这个意义上讲,Realm 实质上是一个安全相关的 DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给 Shiro 。当配置 Shiro时,你必须至少指定一个 Realm ,用于认证和(或)授权。配置多个 Realm 是可以的,但是至少需要一个。
Shiro 内置了可以连接大量安全数据源(又名目录)的 Realm,如 LDAP、关系数据库(JDBC)、类似 INI 的文本配置资源以及属性文件等。如果缺省的 Realm 不能满足需求,你还可以插入代表自定义数据源的自己的 Realm 实现。

功能

Realm能做的工作主要有以下几个方面:

  • 身份验证(getAuthenticationInfo 方法)验证账户和密码,并返回相关信息

  • 权限获取(getAuthorizationInfo 方法) 获取指定身份的权限,并返回相关信息

  • 令牌支持(supports方法)判断该令牌(Token)是否被支持

    令牌有很多种类型,例如:HostAuthenticationToken(主机验证令牌),UsernamePasswordToken(账户密码验证令牌)

这里主来说明一下关于前两点验证方面的逻辑,因为令牌一般用的都是 UsernamePasswordToken,哪怕用 HostAuthenticationToken,也没必要细讲,这个函数很少用到。

身份验证

我们看到第一个方法就是我们上面说的“验证账户和密码,并返回相关信息”的方法。从方法的名字上看,只有取得验证信息的意思,其实这里面还包括了进行验证的逻辑。
看Javadoc,这个方法的作用是:根据传进来的 Token,返回用户的验证信息。下面说明一下 Token 和 用户验证信息 。

  • Token:就是要拿来进行验证的信息,例如:如果是 UsernamePasswordToken 的话,这个 Token 的内容就是“用户提交的用户名和密码”。

    来看下 UsernamePasswordToken 的属性。

    public class UsernamePasswordToken implements HostAuthenticationToken, RememberMeAuthenticationToken {private String username;private char[] password;private boolean rememberMe;private String host;
    ...

  • 用户验证信息:就是用户验证通过后,返回给系统的信息。例如:用户登录验证的话,一般来说,返回给系统的“用户验证信息”就应该是这个用户的“用户名和密码”。但也可以返回其它信息,例如返回用户的“邮箱地址和登录密码”信息,做为“用户验证信息”。 那么返回给谁呢,Shiro 中的三大组件之一的 Subject。

    不细谈,这么说吧,Subject:即“当前操作用户”。但是,在 Shiro 中,Subject 这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是 Shiro 的“用户”概念。

上面说了“根据传进来的Token”和“返回用户的验证信息”,但没有说验证的过程,这个过程也是在这个方法中进行。我们看一下源码:

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {AuthenticationInfo info = getCachedAuthenticationInfo(token);// doGetAuthenticationInfo方法的内容,由各个子类来实现。// 主要是用来取得我们保存的“用户验证信息”,例如DB里保存的密码(具体看JdbcRealm的方法实现)if (info == null) {info = doGetAuthenticationInfo(token);...}// 在这里,把用户提交的信息(Token)和我们保存的“用户验证信息”进行比较// 如果不通过,直接抛出定义好的异常。if (info != null) {assertCredentialsMatch(token, info);} else {return info;
}

权限获取

“权限验证”的处理,是由接口定义的。但“验证是否有访问权限”的逻辑,则是由类定义的。定义的类为:AuthorizingRealm ,在这个类中有个getAuthorizationInfo 方法。这个方法和getAuthenticationInfo 方法的处理流程有点像:

  • 验证是否有指定的权限

  • 返回用户的权限信息

调用时机

下面看一个实际登录的 Controller 的例子:

@Controller
public class LoginController {//登录跳转@RequestMapping(value = "/login", method = {RequestMethod.GET})public String loginUI() throws Exception {return "../../login";}//登录跳转@RequestMapping(value = "/sxqy", method = {RequestMethod.GET})public String loginUI2() throws Exception {return "../../login";}//重点!!!!!!//登录表单处理@RequestMapping(value = "/login", method = {RequestMethod.POST})public String login(ViewEmployeeMiPsd viewEmployeeMiPsd) throws Exception {//Shiro实现登录UsernamePasswordToken token = new UsernamePasswordToken(viewEmployeeMiPsd.getCode(),viewEmployeeMiPsd.getPsd());Subject subject = SecurityUtils.getSubject();//如果获取不到用户名就是登录失败,但登录失败的话,会直接抛出异常try{//重点!!!!!!//getAuthenticationInfo 执行时机subject.login(token);}catch (Exception e){e.printStackTrace();}//重点!!!!!!//getAuthorizationInfo  执行时机 -- subject.hasRole()if (subject.hasRole("admin")) {return "redirect:/admin/showComputerProblems";} else if (!subject.hasRole("admin")) {return "redirect:/normal/showComputerProblems";}return "/login";}}

不过,getAuthorizationInfo 的执行调用方式包括上面的总共有三个:

  1. subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;
  2. @RequiresRoles(“admin”) :在方法上加注解的时候;
  3. [@shiro.hasPermission name = “admin”][/@shiro.hasPermission]:在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。

实现

需要注意的是,在 Shiro 实际使用中,我们是肯定会自定义一个 Realm 类的。

从上面的功能说明可以看出来,在权限控制中比较重要的验证(登录或权限)逻辑,都是在Realm中做的。Realm的类继承如下:

不同的继承,需要实现不同的方法。继承了 AuthorizingRealm 的类,都要实现上面说的 getAuthenticationInfogetAuthorizationInfo 方法,来完成身份验证和权限获取。但如果自定义的 Realm 类只实现 Realm 接口的话,只需要 getAuthenticationInfo 方法就可以。下面看一个只实现 Realm 接口的自定义 Realm:

public class MyRealm1 implements Realm {  @Override  public String getName() {  return "myrealm1";  }  @Override  public boolean supports(AuthenticationToken token) {  //仅支持UsernamePasswordToken类型的Token  return token instanceof UsernamePasswordToken;   }  @Override  public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  String username = (String)token.getPrincipal();  //得到用户名  String password = new String((char[])token.getCredentials()); //得到密码  if(!"zhang".equals(username)) {  throw new UnknownAccountException(); //如果用户名错误  }  if(!"123".equals(password)) {  throw new IncorrectCredentialsException(); //如果密码错误  }  //如果身份认证验证成功,返回一个AuthenticationInfo实现;  return new SimpleAuthenticationInfo(username, password, getName());  }
}   

但是在使用中基本上都会对账户进行权限管理,下面看一个继承 AuthorizingRealm 的自定义 Realm:

@Component
public class LoginRealm extends AuthorizingRealm{@SuppressWarnings("SpringJavaAutowiringInspection")//忽略警告,下同@Resource(name = "roleServiceImpl")private RoleService roleService;@SuppressWarnings("SpringJavaAutowiringInspection")//忽略警告,下同@Resource(name = "viewEmployeeMiPsdServiceImpl")private ViewEmployeeMiPsdService viewEmployeeMiPsdService;/***      获取身份信息,我们可以在这个方法中,从数据库获取该用户的权限和角色信息*      当调用权限验证时,就会调用此方法*/protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {String code = (String) getAvailablePrincipal(principalCollection);Role role = null;ViewEmployeeMiPsd viewEmployeeMiPsd = null;viewEmployeeMiPsd = viewEmployeeMiPsdService.findByCode(code);//通过用户名从数据库获取角色权限集SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();Set<String> r = new HashSet<>();if (role != null) {String[] roles = role.getRolename().split("\\+");for(int i = 0;i < roles.length; i++){r.add(roles[i].toString());}//放入该用户权限信息info.setRoles(r);}return info;}/*** 在这个方法中,进行身份验证* login时调用*/protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//工号String code = (String) token.getPrincipal();//密码String password = new String((char[])token.getCredentials());ViewEmployeeMiPsd viewEmployeeMiPsd = null;viewEmployeeMiPsd = viewEmployeeMiPsdService.findByCode(code);if (viewEmployeeMiPsd == null) {//没有该用户throw new UnknownAccountException();} else if (!password.equals(viewEmployeeMiPsd.getPsd())) {//密码错误throw new IncorrectCredentialsException();}//身份验证通过,返回一个身份信息AuthenticationInfo aInfo = new SimpleAuthenticationInfo(code,password,getName());return aInfo;}
}

参考

  1. 关于Shiro中的Realm – hotdust
  2. 关于何时执行shiro AuthorizingRealm 里的 doGetAuthenticationInfo与doGetAuthorizationInfo – fj200821
  3. shiro – 百度百科

Shiro 中的 Realm相关推荐

  1. shiro集成 spring-加密md5配置--权限管理-shiro中的session 等等!!

    目录 1.shiro集成 spring 1.1 导入依赖的包 1.2 web.xml配置 2. 开发自定义Realm 2.1 创建测试数据表 2.3 编写后台支持服务 2.4 编写自定义的Realm ...

  2. Shiro系列-Shiro中Realm如何使用

    导语   之前的分享中,了解到了用户身份认证,在说用户认证的时候提到了一个概念就是Realm,在之前的入门分享中提到了,Realm其实就是一个安全数据源,那么怎么样使用这个安全数据源呢?下面就来一起研 ...

  3. Spring Boot -Shiro配置多Realm

    核心类简介 xxxToken:用户凭证 xxxFilter:生产token,设置登录成功,登录失败处理方法,判断是否登录连接等 xxxRealm:依据配置的支持Token来认证用户信息,授权用户权限 ...

  4. Spring Boot -Shiro配置多Realm 1

    2019独角兽企业重金招聘Python工程师标准>>> 核心类简介 xxxToken:用户凭证 xxxFilter:生产token,设置登录成功,登录失败处理方法,判断是否登录连接等 ...

  5. shiro中基于注解实现的权限认证过程

    授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限. 如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限等等. 一.用户权限模型 为实现一个较为灵活的用户权限数据模 ...

  6. shiro框架多realm登录认证配置

    我们做shiro框架经常会遇到这种情况,用户数量很多,又不在同一个表里,比如管理员一个表,用户一个表,商家一个表.这时我们就需要用到多realm来配置让他们用不同得realm来进行登录 首先来说思路, ...

  7. Shiro验证策略-shiro自定义实现Realm来实现身份验证-shiro散列加密算法-shiro授权-shiro自定义Realm实现授权

    Shiro验证策略 Authentication Strategy:认证策略,在shiro中有三种认证策略: AtleastOneSuccessfulStrategy[默认] 如果一个或多个Realm ...

  8. Shiro 几种Realm的使用,认证、授权

    目录 Shiro的依赖 IniRealm JdbcRealm 自定义Realm 使用示例 常用的Realm有3种 IniRealm:从ini文件加载安全数据 JdbcRealm:从数据库加载安全数据 ...

  9. shiro中的验证用户身份认证以及授权

    目录 1.运用shiro进行用户身份认证: 1.1导入基于Shiro的数据库脚本 1.2.引入依赖(shiro-1.4.1) shiro-core  shiro-web  shiro-spring 1 ...

最新文章

  1. Excel常用公式记录
  2. pytorch强化学习训练倒摆小车
  3. Nacos 1.1.4 发布,业界率先支持 Istio MCP 协议
  4. vue 使用了浏览器的刷新之后报错_Laravel 7 + vue.js 学习笔记(一)
  5. ActiveMQ的集群与高可用
  6. windows之DNS7种资源记录和flushdns命令清除DNS缓存以及nslookup解析域名和ipconfig/all命令查看网络配置使用总结
  7. 时隔多日,旧域名重用,niceyoo博客上线
  8. 2003文件共享服务器搭建,用Windows Server 2003搭建安全文件服务器(2)
  9. [再寄小读者之数学篇](2014-12-24 乘积型不等式)
  10. 书籍:Python游戏开发 Game Development Using Python - 2019.pdf
  11. 微信小程序上线订单管理功能:实现订单管理、售后维权一体化
  12. java 经典免费教程下载
  13. AcWing 100. 增减序列
  14. idea上一步下一步快捷键_领航者的一步,左右行业的下一步!双11海尔洗衣机再夺冠的思考...
  15. Python里面几种排序算法的比较,sorted的底层实现,虽然我们知道sorted的实现方式,但是...
  16. 《Python数据挖掘:概念、方法与实践》一2.4 小结
  17. C++中回调函数(CALLBACK)初探
  18. C#Const与static readonly的区别
  19. axure sketch 对比_Sketch 画原型比 Axure 好用吗?为什么
  20. 读取阿里云服务器图片到本地

热门文章

  1. 研究型论文_T-DFNN:一种用于入侵检测系统的增量学习算法(英文论文)
  2. 边缘计算、雾计算和云计算
  3. thickbox使用技巧 (转)
  4. Jenkins admin 密码忘记解决
  5. Annotation介绍
  6. centos查看正在运行的服务_win7电脑服务器正在运行中的解决教程
  7. 频繁读写剪贴板导致操作失败的bug
  8. oracle flashback 用法,使用oracle flashback database
  9. class10-UCSB.presentation
  10. 4014基于邻接表的长度为k的简单路径的求解