Java的Shiro框架认证流程详解
作者:Splaying
这篇文章主要介绍了Java的Shiro框架认证流程详解,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、项目依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.7.1</version> </dependency>
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; } }
到此这篇关于Java的Shiro框架认证流程详解的文章就介绍到这了,更多相关Shiro框架认证流程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!