首页 > 编程语言 >shiro源码第一天:登陆验证部分

shiro源码第一天:登陆验证部分

时间:2022-12-02 22:39:16浏览次数:36  
标签:realm 第一天 token 源码 import org Realm shiro


登陆验证部分:

  • ​​1.单点登陆系统中不同系统密码验证方式不一样​​

1.单点登陆系统中不同系统密码验证方式不一样

问题:当使用shiro作为鉴权框架时,首先用到的应该就是登陆认证了,登录成功后的操作再根据用户权限来判定。

如果只是单系统用户,直接定义一个Realm,来进行用户的登录和鉴权。登录直接对用户密码的验证,验证成功则告诉shiro,然后放行。

如果用在单点登陆系统中,则一个简单的登录验证就有可能会比较麻烦。因为不同系统用户不同,并且密码的加密验证方式也有可能不同,如果用同一个验证规则对用户的登录来源进行判断,就需要在同一个接口里面进行各种​​if else​​判断,这样的方式可能不太优雅。既然已经用了shiro框架了,那么就来看下使用shiro怎么优雅的解决这个问题吧。

既然这时一个Realm则可能不能满足登陆验证的需求了。那么能不能定义多个Realm,然后动态的让对应的Realm去处理对应的验证机制,这样代码的入侵性就比较小了,当增加一种新的系统,我们直接添加对应的Realm即可,不会影响到别的。

解决方案:可以自定义多个Realm,然后将realm都传入​​SecurityManager​​​。
MyRealm1.java

package com.lin.test.chapter2.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;
import org.slf4j.LoggerFactory;

public class MyRealm1 implements Realm {

org.slf4j.Logger log = LoggerFactory.getLogger(getClass());

@Override
public String getName() {
// TODO Auto-generated method stub
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(" UnknownAccountException Msg");//如果用户名错误
}
if (!"123".equals(password)) {
throw new IncorrectCredentialsException("IncorrectCredentialsException Msg");//如果密码错误
}
//如果身份认证验证成功,返回一个AuthenticationInfo实现;
log.info("wang 123 login success");
return new SimpleAuthenticationInfo(username, password, getName());
}

}

MyRealm2.java

package com.lin.test.chapter2.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;
import org.slf4j.LoggerFactory;

public class MyRealm2 implements Realm {

org.slf4j.Logger log = LoggerFactory.getLogger(getClass());

@Override
public String getName() {
// TODO Auto-generated method stub
return "myRealm2";
}

@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 (!"wang".equals(username)) {
throw new UnknownAccountException();//如果用户名错误
}
if (!"123".equals(password)) {
throw new IncorrectCredentialsException();//如果密码错误
}
//如果身份认证验证成功,返回一个AuthenticationInfo实现;
log.info("zhang 123 login success");
return new SimpleAuthenticationInfo(username, password, getName());
}

}

  以上demo中,自定义 Realm直接继承了Realm类,在使用的时候我们 一般继承 ​​AuthorizingRealm​​​ 就可以了,我们可以看下该类的继承关系:其继承了​​AuthenticatingRealm​​​(即身份验证),而且也间接继承了​​CachingRealm​​​(带有缓存实现),最顶端的父类还是​​Realm​​​。这里自定义之后要想让shiro知道我们自定义的,还必须将自定义的Realm传入​​SecurityManager​​中才会生效。

  如果定义了多个 realm 则 shiro 会根据 ​​securityManager​​​ 中设置的顺序 来以此进行 realm 的验证。 每个realm验证成功后返回一个 ​​SimpleAuthenticationInfo(username, password, getName())​​​ 对象,​​getName​​方法可在realm中自定义实现,如返回realm的名称,验证失败则抛出对应的异常。

  当​​securityManager​​​中传入了多个realm时,在执行​​subject.login(usernamePasswordToken);​​​后,可以使用subject.getPrincipals(); 得到一个身份集合,其包含了Realm验证成功的身份信息。可以对返回的身份集合的大小(​​principalCollection.asList().size()​​)进行判断:全部满足,部分满足,至少一个满足等.

(具体见源码:​​org.apache.shiro.authc.pam.ModularRealmAuthenticator.doMultiRealmAuthentication(Collection<Realm>, AuthenticationToken))​​。

当需要对多个​​realm​​​执行后的结果进行判断时,可以自定义 ​​AuthenticationStrategy​​​ 实现。
其api包含:

  • ​beforeAllAttempts​​在所有Realm验证之前调用
  • ​beforeAttempt​​在每个Realm之前调用
  • ​afterAttempt​​在每个Realm之后调用
  • ​afterAllAttempts​​在所有Realm之后调用

可以看到源码​​ModularRealmAuthenticator.doMultiRealmAuthentication()​​​方法中​​AuthenticationInfo​​ 只有一个实例。

/**
* Performs the multi-realm authentication attempt by calling back to a {@link AuthenticationStrategy} object
* as each realm is consulted for {@code AuthenticationInfo} for the specified {@code token}.
*
* @param realms the multiple realms configured on this Authenticator instance.
* @param token the submitted AuthenticationToken representing the subject's (user's) log-in principals and credentials.
* @return an aggregated AuthenticationInfo instance representing account data across all the successfully
* consulted realms.
*/
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {

AuthenticationStrategy strategy = getAuthenticationStrategy();

AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);

if (log.isTraceEnabled()) {
log.trace("Iterating through {} realms for PAM authentication", realms.size());
}

for (Realm realm : realms) {

aggregate = strategy.beforeAttempt(realm, token, aggregate);

if (realm.supports(token)) {

log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);

AuthenticationInfo info = null;
Throwable t = null;
try {
info = realm.getAuthenticationInfo(token);
} catch (Throwable throwable) {
t = throwable;
if (log.isWarnEnabled()) {
String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";
log.warn(msg, t);
}
}

aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);

} else {
log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token);
}
}

aggregate = strategy.afterAllAttempts(token, aggregate);

return aggregate;
}

这是因为每个​​AuthenticationStrategy​​实例都是无状态的,所以每次都通过接口将相应的认证信息传入下一次流程;通过如上接口可以进行如合并/返回第一个验证成功的认证信息。

自定义 ​​AuthenticationStrategy​​​ 实现时一般继承​​org.apache.shiro.authc.pam.AbstractAuthenticationStrategy​​​ 即可,shiro提供了三种实现:​​AllSuccessfulStrategy​​​,​​AtLeastOneSuccessfulStrategy​​​,​​FirstSuccessfulStrategy​

当时用单点登陆时,可以使用​​AtLeastOneSuccessfulStrategy​​这个类,至少保证有一个认证成功,才能算登陆成功。

但是这样又带来了额外的压力,因为遍历了所有的realm,要是我们能在用户进来时,提前知道他来自哪个系统,然后直接告诉shiro,用那种系统对应的realm的话,就能省事多了。

当一个用户过来了,我们可以提前知道

很好的shiro系列博客:​​http://www.iocoder.cn/Shiro/good-collectio​


标签:realm,第一天,token,源码,import,org,Realm,shiro
From: https://blog.51cto.com/linmengmeng/5907635

相关文章

  • EasyExcel 实践与源码梳理
    目录​​1.写在最前​​​​1.1EasyExcel版本​​​​1.2初探源码​​​​2.表头实体类MyUser​​​​3.最简单的导出Excel文件​​​​4.源码demo:​​​​4.1读Exc......
  • 学习Spring源码问题总结
    记录一下学习源码遇到的问题:​​1.编译时报错:Failedtoapplyplugin[id'com.gradle.build-scan']​​​​2.报错Groovy:compilermismatchprojectlevelis:2.4Works......
  • (旧)springboot 快速实现登录、注册功能(附Demo源码)
    1.直接跑通Demo,修改配置文件。导入数据库sql文件即可。2.跟着一步一步实现。当然你也可以先跑通Demo,在尝试自己跟着来一遍1.跑通Demo需要源码和Demo跳转新项目跳转新......
  • Dubbo源码-11-服务引用流程
    一入口publicstaticvoidmain(String[]args){//引用远程服务此实例很重封装了与注册中心的连接以及与提供者的连接ReferenceConfig<DemoServic......
  • Dubbo源码-12-Cluster
    一接口声明@SPI(FailoverCluster.NAME)publicinterfaceCluster{/***Mergethedirectoryinvokerstoavirtualinvoker.**@param<T>......
  • Bert源码学习
    文章目录​​前言​​​​1.bert模型网络modeling.py​​​​1.1整体架构BertModel(object):​​​​1.2embedding层​​​​1.2.1embedding_lookup​​​​1.2.2词向......
  • Shiro权限 表设计
    表设计权限管理需要的基本表有5个如下:sys_users用户表sys_roles角色表sys_permissions权限表(或资源表)sys_users_roles用户-角色关联表sys_roles_permissions角色-......
  • 源码注释中的"方法签名"是什么意思?
    我们经常可以在源码注释中看到methodsignature,也就是方法签名,那它指的是方法中的哪部分呢?好比@Async中的第二段注释中《Java语言程序设计》一书中对方法的描述中有......
  • 直播系统app源码,设置样式(字体样式、行列宽高、对齐方式、边框)
    直播系统app源码,设置样式(字体样式、行列宽高、对齐方式、边框)1.字体样式 fromopenpyxlimportWorkbookfromopenpyxl.stylesimportFontwb=Workbook()ws=wb.act......
  • Mysql 源码解读-执行器
    Mysql源码解读-执行器一条sql执行过程中,首先进行词法分析和语法分析,然后将由优化器进行判断,如何执行更有效率,生成执行计划,后面的任务就交给了执行器。在执行的过程中,执......