在UserController中调用subject.login(token)方法

@Controller
@RequestMapping("/user")
public class UserController extends MyExceptionHandler {@RequestMapping("/login")@ResponseBody//把前端传进来的数据自动映射到user captcha不能自动映射public Result<Object> login(User user, String captcha) {System.out.println(user);//System.out.println(captcha);//引入shiro的加密算法   四个参数(参数1 采用何种加密算法  参数2 传进来要加密的值   参数3 盐值  参数4 加密的次数   )String pwd= new SimpleHash("MD5", user.getPwd(), user.getAccount(), 218).toString();// 认证// 1.获取subject对象Subject currentUser = SecurityUtils.getSubject();//2.判断是否认证过if (!currentUser.isAuthenticated()) {//没认证创建令牌UsernamePasswordToken token = new UsernamePasswordToken(user.getAccount(), pwd);try {currentUser.login(token);  // <----调用subject.login(token)System.out.println("认证成功");} catch (UnknownAccountException e) {System.out.println("帐号不存在");Result<Object> result = new Result<>().setStatus(Result.ERROR);return result;} catch (IncorrectCredentialsException e) {System.out.println("密码错误");Result<Object> result = new Result<>().setStatus(Result.ERROR);return result;} catch (Exception e) {System.out.println("其他异常");Result<Object> result = new Result<>().setStatus(Result.ERROR);return result;}}Result<Object> result = new Result<>().setStatus(Result.SUCCESS);return result;}
}

追踪Subject.login()方法,其调用的是DelegatingSubject的login方法,DelegatingSubject实现subject接口


public class DelegatingSubject implements Subject { public void login(AuthenticationToken token) throws AuthenticationException {clearRunAsIdentitiesInternal();Subject subject = securityManager.login(this, token); // <--在调用方法PrincipalCollection principals; String host = null;if (subject instanceof DelegatingSubject) {DelegatingSubject delegating = (DelegatingSubject) subject;//we have to do this in case there are assumed identities - we don't want to lose the 'real' principals:principals = delegating.principals;host = delegating.host;} else {principals = subject.getPrincipals();}if (principals == null || principals.isEmpty()) {String msg = "Principals returned from securityManager.login( token ) returned a null or " +"empty value.  This value must be non null and populated with one or more elements.";throw new IllegalStateException(msg);}this.principals = principals;this.authenticated = true;if (token instanceof HostAuthenticationToken) {host = ((HostAuthenticationToken) token).getHost();}if (host != null) {this.host = host;}Session session = subject.getSession(false);if (session != null) {this.session = decorate(session);} else {this.session = null;}} }

在上面代码第4行 Subject subject = securityManager.login(this, token);调用的是SecurityManager的login方法,SecurityManager是接口,实际调用的是DefaultSecurityManager中的login方法

public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {AuthenticationInfo info;try {info = authenticate(token);} catch (AuthenticationException ae) {try {onFailedLogin(token, ae, subject);} catch (Exception e) {if (log.isInfoEnabled()) {log.info("onFailedLogin method threw an " +"exception.  Logging and propagating original AuthenticationException.", e);}}throw ae; //propagate}Subject loggedIn = createSubject(token, info, subject);onSuccessfulLogin(token, info, loggedIn);return loggedIn;}

上面代码第4行 info = authenticate(token);去认证用户信息,继续跟踪,发现调用的父类AuthenticatingSecurityManager中的authenticate方法

public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {return this.authenticator.authenticate(token);}

上面代码第二行 this.authenticator.authenticate(token);继续跟踪
调用的是Authenticator接口中的抽象类AbstractAuthenticator中的
authenticate()方法

 public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {if (token == null) {throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");}log.trace("Authentication attempt received for token [{}]", token);AuthenticationInfo info;try {info = doAuthenticate(token);if (info == null) {String msg = "No account information found for authentication token [" + token + "] by this " +"Authenticator instance.  Please check that it is configured correctly.";throw new AuthenticationException(msg);}} catch (Throwable t) {AuthenticationException ae = null;if (t instanceof AuthenticationException) {ae = (AuthenticationException) t;}if (ae == null) {//Exception thrown was not an expected AuthenticationException.  Therefore it is probably a little more//severe or unexpected.  So, wrap in an AuthenticationException, log to warn, and propagate:String msg = "Authentication failed for token submission [" + token + "].  Possible unexpected " +"error? (Typical or expected login exceptions should extend from AuthenticationException).";ae = new AuthenticationException(msg, t);if (log.isWarnEnabled())log.warn(msg, t);}try {notifyFailure(token, ae);} catch (Throwable t2) {if (log.isWarnEnabled()) {String msg = "Unable to send notification for failed authentication attempt - listener error?.  " +"Please check your AuthenticationListener implementation(s).  Logging sending exception " +"and propagating original AuthenticationException instead...";log.warn(msg, t2);}}throw ae;}log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);notifySuccess(token, info);return info;}

上面代码第11行info = doAuthenticate(token);继续跟踪,调用的是其子类ModularRealmAuthenticator中的doAuthenticate()方法
要注意如果 info==null 会抛异常

 protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {assertRealmsConfigured();Collection<Realm> realms = getRealms();if (realms.size() == 1) {return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);} else {return doMultiRealmAuthentication(realms, authenticationToken);}}

上面代码第5行doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);继续跟踪

 protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {if (!realm.supports(token)) {String msg = "Realm [" + realm + "] does not support authentication token [" +token + "].  Please ensure that the appropriate Realm implementation is " +"configured correctly or that the realm accepts AuthenticationTokens of this type.";throw new UnsupportedTokenException(msg);}AuthenticationInfo info = realm.getAuthenticationInfo(token);if (info == null) {String msg = "Realm [" + realm + "] was unable to find account data for the " +"submitted AuthenticationToken [" + token + "].";throw new UnknownAccountException(msg);//错误的账号}return info;}

上面代码第9-12行 if(info ==null){
throw new UnknownAccountException(msg);
}表示从数据库没有查找此账号,会抛出错误的账号异常
上面代码第8行 AuthenticationInfo info = realm.getAuthenticationInfo(token);继续跟踪 调用的是Realm中的抽象类AuthenticatingRealm的getAuthenticationInfo()方法

 public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {AuthenticationInfo info = getCachedAuthenticationInfo(token); //从缓存中拿数据if (info == null) {//如果没有从缓存中找到数据,说明未登陆,用下面的方法去查找info = doGetAuthenticationInfo(token);log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);if (token != null && info != null) {cacheAuthenticationInfoIfPossible(token, info);}} else {log.debug("Using cached authentication info [{}] to perform credentials matching.", info);}if (info != null) {assertCredentialsMatch(token, info);} else {log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);}return info;}

上面代码第5行info = doGetAuthenticationInfo(token);继续跟踪,调用的是AuthenticatingRealm的实现类SimpleAccountRealm的 doGetAuthenticationInfo()方法

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {UsernamePasswordToken upToken = (UsernamePasswordToken) token; //强转为UsernamePasswordToken 用于令牌认证SimpleAccount account = 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个异常,锁定的账号异常 过期的账号异常,如果我们自己自定义的类继承了AuthorizingRealm 并实现了doGetAuthenticationInfo()方法,则会调用我们自己类中方法进行认证


public class MyRealm  extends AuthorizingRealm{//注入service@Autowiredprivate UserService userService;@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("正在进行认证。。。");//获取到账号,然后通过账号作为查询条件String account = (String) token.getPrincipal();String pwd = new String((char[]) token.getCredentials()) ;System.out.println(account +","+pwd);//调用service方法查询数据库
//  User user=new User().setAccount("admin").setPassword("e0710a93d5e494c1a86b621700e682c5");User user=userService.findUserByAccount(account);if(user==null){return null;}  SimpleAuthenticationInfo info =new SimpleAuthenticationInfo(user.getAccount(), user.getPwd(), getName()) ; return info;}}

此时doGetAuthenticationInfo()方法已执行完毕,回到AuthenticatingRealm的getAuthenticationInfo()方法中,再次复制AuthenticatingRealm的getAuthenticationInfo()中的代码

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {AuthenticationInfo info = getCachedAuthenticationInfo(token);if (info == null) {//otherwise not cached, perform the lookup:info = doGetAuthenticationInfo(token); //此方法已走完log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);if (token != null && info != null) {cacheAuthenticationInfoIfPossible(token, info);}} else {log.debug("Using cached authentication info [{}] to perform credentials matching.", info);}//此时info!=null 运行以下代码if (info != null) {assertCredentialsMatch(token, info);} else {log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);}return info;}

上面代码的第16行assertCredentialsMatch(token, info);继续跟踪

protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {CredentialsMatcher cm = getCredentialsMatcher();if (cm != null) {if (!cm.doCredentialsMatch(token, info)) {//not successful - throw an exception to indicate this: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.");}}

上面代码第4行 if (!cm.doCredentialsMatch(token, info)) 去判断密码是否正确,如果错误,这抛 密码错误异常throw new IncorrectCredentialsException(msg); 继续跟踪密码判断 调用CredentialsMatcher的实现类中的 SimpleCredentialsMatcher 中的doCredentialsMatch()

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

上面代码中第2-3行去获取token和info中的密码,在调用equals()方法
继续跟踪getCredentials()

 protected Object getCredentials(AuthenticationToken token) {return token.getCredentials();}

上面第2行代码 token.getCredentials(); 继续跟踪 调用的是AuthenticationToken子类HostAuthenticationToken的实现类UsernamePasswordToken中的getCredentials()方法

 public Object getCredentials() {return getPassword();}
public char[] getPassword() {return password;}
 protected boolean equals(Object tokenCredentials, Object accountCredentials) {if (log.isDebugEnabled()) {log.debug("Performing credentials equality check for tokenCredentials of type [" +tokenCredentials.getClass().getName() + " and accountCredentials of type [" +accountCredentials.getClass().getName() + "]");}if (isByteSource(tokenCredentials) && isByteSource(accountCredentials)) {if (log.isDebugEnabled()) {log.debug("Both credentials arguments can be easily converted to byte arrays.  Performing " +"array equals comparison");}byte[] tokenBytes = toBytes(tokenCredentials);byte[] accountBytes = toBytes(accountCredentials);return MessageDigest.isEqual(tokenBytes, accountBytes);} else {return accountCredentials.equals(tokenCredentials);}}

上面倒数第2行代码 判断密码是否相等

shiro 调用 subject.login(token)方法后相关推荐

  1. shiro的登录 subject.login(token)中执行逻辑和流程

    文章目录 官方文档中的介绍 使用subject.login的登录场景: DelegatingSubject实现类中的login方法: DefaultSecurityManager Authentica ...

  2. vue 子组件调用($emit)父组件方法后父组件方法如何回调子组件方法

    子级组件中的实现 组件名称:EditTable.vue export default{ name:'ET', ....... methods:{ ShowMore(step){             ...

  3. 解决Ranorex在测试执行过程中,当执行完调用外界库的方法后并没有执行其他的操作?

    目录 简介 解决方式-1 解决方式-2 如何调用外界库? 简介 解决:当Ranorex测试方案包含有调用外界库的方法和其他通过录制或者其他操作的前提下,执行测试过程中,如果遇到执行完调用外界库的方法, ...

  4. subject.login(token)是如何确认账号密码的_教你如何删除、关闭、注销微信小程序...

    微信小程序是我们日常生活中经常会接触到的工具,打开小程序后,它就会留在我们微信的""发现-小程序"栏.很多人并不知道该如何删除.关闭小程序,所以今天就跟大家科普下相关问题 ...

  5. MediaPlay调用start和stop方法后,音频再次点击不能播放

    参考链接如下 https://blog.csdn.net/fcw_one/article/details/109076113 解决办法: 将stop方法改为pause方法即可

  6. Shiro的认证原理(Subject#login的背后故事)

    登录操作一般都是我们触发的: Subject subject = SecurityUtils.getSubject(); AuthenticationToken authenticationToken ...

  7. java清除shiro里的token_shiro token 分析

    1.ShiroConfig.java 定义匿名用户可以访问的资源 filterMap.put("/webjars/**", "anon"); filterMap ...

  8. java调用一个方法后怎么继续执行不等待该方法的返回_Java面试题大全2020版(二)...

    今天给大家推送第二部分,主要的大块内容分为:多线程.反射.对象拷贝.三大块内容中涉及到的考点如下: 三.多线程 35. 并行和并发有什么区别? 并行是指两个或者多个事件在同一时刻发生:而并发是指两个或 ...

  9. springboot项目中使用shiro 自定义过滤器和token的方式___shiro使用token登录流程

    springboot项目中使用shiro 自定义过滤器和token的方式 实现步骤主要是以下几步: 1. 在项目中导入maven依赖 <dependency><groupId> ...

  10. Shiro的subject实质上是当前执行用户的特定视图。

    Shiro的subject实质上是当前执行用户的特定视图. 通过org.apache.shiro.SecurityUtils可以查询当前执行用户: Subject currentUser = Secu ...

最新文章

  1. 全球支付平台paypal社招一面,二面合并面经
  2. Java虚拟机 —— 类的加载机制
  3. 上传ML模型,一键可视化,终于能看懂神经网络到底在干啥了
  4. 关于bcp的那些事儿
  5. sublime Package Control 设备
  6. leetcode C++ 2. 两数相加 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 如果,我们将这两个数
  7. c 内嵌php 韩天峰,PHP-X系列教程之内置函数的使用示例
  8. codeforces contest 1140(D~G)
  9. 3行Python代码完成人脸识别
  10. 使用identity+jwt保护你的webapi(一)——identity基础配置
  11. C# 去除所有的html标签
  12. PCI_Express规范第七章解读-Software Initialization and configuration
  13. 卷积网络虽动人,胶囊网络更传“神”
  14. 【综合篇】Web前端性能优化原理问题
  15. oracle 建表 varchar,一个完整的Oracle建表的例子
  16. ora 01033 linux,数据库ORA-01033错误解决办法
  17. TCP粘包以及UDP丢包问题
  18. 轻松学会Python列表解析式
  19. springboot系列(5) -- 整合 logback 彩色日志输
  20. Win10安装过程中如何跳过创建Microsoft账户

热门文章

  1. 雀巢咖啡旗下感CAFÉ推出高端鎏光咖啡;武田剥离中国大陆非核心业务至海森 | 美通企业日报...
  2. 一文搞懂X509证书PEM DER CRT CER的区别
  3. 10家不错的iphone编程资源站
  4. RK987蓝牙键盘使用说明书分享
  5. 使用UltraISO制作光盘镜像
  6. Xcode iOS开发:UIKit常用组件之按钮控件
  7. 在线网站\本地软件拓扑图\复杂网络绘制
  8. prometheus+grafana搭建监控平台监控压测服务器mysql性能
  9. 轻量易用的网站bug与性能监控平台——灵雀应用监控平台
  10. 依分布收敛、依概率收敛、均方收敛、几乎处处收敛