栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Shiro框架认证流程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Shiro框架认证流程

1、Shiro框架介绍

  • Shiro 是一个功能强大和易于使用的安全框架,为开发人员提供一个直观而全面的解决方案的认证,授权,加密,会话管理四大功能!

  • Subject:主体,subject记录了当前操作用户,将当前登录访问的用户信息存在其中。

  • Authenticator:身份认证/登录,验证用户账号密码是否正确。

  • Authorizer:授权,根据不同的用户发放不同的权限。

  • SessionManager:会话管理,它不依赖web容器的session,因此Shiro也可以使用在非web应用上独立使用

  • Realm:领域范围,securityManager进行安全认证需要通过Realm获取数据,然后与Subject中的数据进行认证授权!

  • Cryptography:加密功能,将隐私数据进行加密。

  • SessionDAO:会话dao,是对session会话操作的一套接口,可以通过jdbc将会话数据存储到数据库。

  • CacheManager:缓存管理,Shiro提供的缓存机制;为了提高效率。


2、入门案例 2.1、项目依赖

    org.apache.shiro
    shiro-core
    1.7.1


2.2、模拟数据
  • resource/shiro.ini文件

  • 一定是ini配置文件,并且使用键值对的形式

[users]
zhangsan=123
admin=123456
splay=123456
2.3、案例
  • 可以看到Shiro最核心的东西是SecurityManager安全管理器

  • 然后初始化ini中的数据给Realm、Realm的数据正常是从数据库中获取的。

  • 将SecurityManager安全管理器注入到SecurityUtil全局工具类中

  • 最后创建令牌模拟用户登录进行校验!

  • 当用户名不正确时抛出UnknownAccountException异常、密码不对时抛出IncorrectCredentialsException异常!

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;

public class Application {

    public static void main(String[] args) {

        // 1. 创建安全管理器对象
        DefaultSecurityManager securityManager = new DefaultSecurityManager();

        // 2. 给安全管理器对象设置realm(模拟数据库中的数据)
        securityManager.setRealm(new IniRealm("classpath:shiro.ini"));

        // 3. SecurityUtil设置全局的安全管理器
        SecurityUtils.setSecurityManager(securityManager);

        // 4. 关键对象Subject主体对象
        Subject subject = SecurityUtils.getSubject();

        // 5. 模拟登录(用户名、密码)
        UsernamePasswordToken user = new UsernamePasswordToken("splay", "123456");

        try{
            System.out.println("认证状态: " + subject.isAuthenticated());
            //登录校验
            subject.login(user);
            System.out.println("认证状态: " + subject.isAuthenticated());
        } catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("认证失败: 用户名密码错误!");
        } catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("认证失败: 用户名错误!");
        }

    }
}

3、源码解析
  • Shiro对整个登录用户的认证分为两个步骤:先校验用户名、其次校验密码;

  • 主要涉及三个类:AuthenticatingRealm、AuthorizingRealm、SimpleAccountRealm

3.1、用户名校验
  • 这里只校验账户名是否被锁定、证书是否过期这两项。

  • 并且这里的account返回为空是可以的。

public class SimpleAccountRealm extends AuthorizingRealm {
	protected SimpleAccount getUser(String username) {
	    USERS_LOCK.readLock().lock();				//ReentrantReadWriteLock可重入读写锁
	    try {
	        return this.users.get(username);
	    } finally {
	        USERS_LOCK.readLock().unlock();
	    }
	}
	
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)  {
	    UsernamePasswordToken upToken = (UsernamePasswordToken) token;
	    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;
	}
}
3.2、密码校验
  • 当用户校验返回值不为空时说明账户存在、没有被锁定、证书没有过期时进行密码校验。
public abstract class AuthenticatingRealm extends CachingRealm implements Initializable {

	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);
        }

        if (info != null) {
            assertCredentialsMatch(token, info);		//见下方
        } else {
            log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
        }

        return info;
    }
    protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
        CredentialsMatcher cm = 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.");
        }
    }
}
3.3、账户不存在异常
  • 这里调用3.2中的方法、3.2中的调用3.1;实则Shiro封装了很多层这里只是最重要的代码。
public class ModularRealmAuthenticator extends AbstractAuthenticator {
	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;
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/298783.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号