一、架构
要学习如何使用Shiro必须先从它的架构谈起,作为一款安全框架Shiro的设计相当精妙。Shiro的应用不依赖任何容器,它也可以在JavaSE下使用。但是最常用的环境还是JavaEE。下面以用户登录为例:
(1)使用用户的登录信息创建令牌
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token可以理解为用户令牌,登录的过程被抽象为Shiro验证令牌是否具有合法身份以及相关权限。
(2)执行登陆动作
SecurityUtils.setSecurityManager(securityManager); // 注入SecurityManager
Subject subject = SecurityUtils.getSubject(); // 获取Subject单例对象
subject.login(token); // 登陆
Shiro的核心部分是SecurityManager,它负责安全认证与授权。Shiro本身已经实现了所有的细节,用户可以完全把它当做一个黑盒来使用。SecurityUtils对象,本质上就是一个工厂类似Spring中的ApplicationContext。Subject是你目前所设计的需要通过Shiro保护的项目的一个抽象概念。通过令牌(token)与项目(subject)的登陆(login)关系,Shiro保证了项目整体的安全。
从securityUtils 中获取当前subject(subject可以理解为用户,也可以理解为当前用户),subject需要被认证,才能访问项目。
身份验证的步骤:
1、收集用户身份/凭证,即如用户名/密码;
2、调用Subject.login进行登录,如果失败将得到相应的AuthenticationException异常,根据异常提示用户错误信息;否则登录成功;
3、最后调用Subject.logout进行退出操作。
流程如下:
1、首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;
2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;
3、Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;
4、Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
5、Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。
(3) realm
@Override
protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken token) throws AuthenticationException {
final String username = (String) token.getPrincipal();
LOGGER.debug("wwwwwwwwwwwwwwwwwwwwwwww1 username=" + username);
final SysUser user = sysUserDao.getFindByUsername(username);
// SysUser user = new SysUser();
if (user == null) {
//没找到帐号
throw new UnknownAccountException();
}
LOGGER.debug("wwwwwwwwwwwwwwwwwwwwwwww realm");
LOGGER.debug("wwwwwwwwwwwwwwwwwwwwwwww2 username=" + user.getUsername() + "||pwd=" + user.getPassword());
if(Boolean.TRUE.equals(user.getLocked())) {
//帐号锁定
throw new LockedAccountException();
}
//交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现
final SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
//用户名
user.getUsername(),
//密码
user.getPassword(),
//salt=username+salt
ByteSource.Util.bytes(user.getCredentialsSalt()),
//realm name
getName()
);
return authenticationInfo;
}
返回一个从数据库里查询的正确的 认证信息 authenticationInfo ,交给 AuthenticatingRealm 使用CredentialsMatcher进行密码匹配。
(4)CredentialsMatcher