Shiro源码阅读之login功能

Shiro源码阅读之login功能

文章目录

  • Shiro源码阅读之login功能
    • controller的实现
    • 源码实现解析

controller的实现

@RequestMapping("/login")
@ResponseBody
public JsonData login(@RequestBody LoginDto loginDto) throws AuthenticationException {
     
    Subject subject = SecurityUtils.getSubject();
    subject.logout();
    if (!subject.isAuthenticated()) {
     
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginDto.getUsername(),
                loginDto.getPassword());

        subject.login(usernamePasswordToken);
        User user = (User) SecurityUtils.getSubject().getPrincipal();

        userService.update(new UpdateWrapper<User>().eq("id", user.getId()).set("last_login", LocalDateTime.now()));

        return JsonData.success("登录成功", MapUtil.builder()
                .put("id", user.getId())
                .put("avatar", user.getAvatar())
                .put("username", user.getUsername())
                .put("lastLogin", user.getLastLogin())
                .put("role", user.getRole()).map());
    }
    return JsonData.fail("请稍后重试");
}

将输入的账号与密码封装入UsernamePasswordToken类,通过SecurityUtils.getSubject().login(token)实现登录功能。下文将深入shiro源码,查看是如何实现登录功能的。

源码实现解析

Shiro源码阅读之login功能_第1张图片
跟进login方法,发现通过this.securityManager.login(this, token);完成登录,并返回Subject对象。
Shiro源码阅读之login功能_第2张图片
继续跟进login方法,token又被传入了this.authenticate()方法,而该方法由AuthenticatingSecurityManager类实现,UML显示,AuthenticatingSecurityManager是该login方法所在类DefaultSecurityManager的父类。
Shiro源码阅读之login功能_第3张图片
在这里插入图片描述
AuthenticatingSecurityManager中,token继续被传递到AbstractAuthenticator类的authenticate方法,该类是Authenticator的实现类。
Shiro源码阅读之login功能_第4张图片
token被继续传递,最终到达ModularRealmAuthenticator类,进入以下代码

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
     
		// 判断是否已经有定义的Realm类,如果没有抛出异常 具体代码见①
        this.assertRealmsConfigured();
        Collection<Realm> realms = this.getRealms();
        // 选择一个realm进行认证,详细见②
        return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
    }

// ①
protected void assertRealmsConfigured() throws IllegalStateException {
     
        Collection<Realm> realms = this.getRealms();
        if (CollectionUtils.isEmpty(realms)) {
     
            String msg = "Configuration error:  No realms have been configured!  One or more realms must be present to execute an authentication attempt.";
            throw new IllegalStateException(msg);
        }
    }

// ②
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);
        } else {
     
            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);
            } else {
     
                return info;
            }
        }
    }

realm.getAuthenticationInfo(token);方法继续传递token。
Shiro源码阅读之login功能_第5张图片
可以发现,会先执行this.getCachedAuthenticationInfo(token);先去cache中查找用户信息,如果是首次登陆查找不到信息,则执行this.doGetAuthenticationInfo(token);,我们需要定义一个realm类,重写doGetAuthenticationInfo对其进行逻辑判断。另外realm类需要继承原方法类AuthenticatingRealm
在重写的方法中,我们需要做的就是通过token的用户名去数据库中查询密码,并且封装成AuthenticationInfo返回。
Shiro源码阅读之login功能_第6张图片
获取到info后,this.assertCredentialsMatch(token, info);进行密码校验。
在这里插入图片描述
进入cm.doCredentialsMatch(token, info)方法。

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

至此,密码比较完成,返回true或false

你可能感兴趣的:(源码阅读,shiro)