认证含义
身份认证就是在应用程序中证明张三就是张三,李四就是李四。通常的逻辑是校验当前登录用户提供的什么认证信息与注册时或者在数据库中存储的信息是否一致。一般是系统用户提供如他们的身份 ID 一些标识信息来表明他就是他本人,如提供身份证,用户名 / 密码来证明。
在Shiro中用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份。
Principle:身份,用来标识用户信息主体,需要具有明确的含义可以用来标识用户信息,如用户名、邮箱、手机号、身份证号等待,只需要保证唯一即可,一个主体一般可以有多个 principles 但是一个用户只能有一个 primary principle,需要使用用户的唯一身份标识来确定到用户,一般是手机号、用户名、身份证号码居多(注意敏感信息需要系统进行脱敏)。
Credentials:证明、凭证、密码等等,为主体 principle的验证信息,需要判断 principle 是否合法,因此需要确保是只有主体知道,如:密码、数字证书、秘钥等等。
环境准备
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
ini配置文件方式
[users]
zhang=123
wang=123
测试用例
@Test
public void testHelloworld() {
//1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2、得到SecurityManager实例 并绑定给SecurityUtils
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
//4、登录,即身份验证
subject.login(token);
} catch (AuthenticationException e) {
//5、身份验证失败
}
Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录
//6、退出
subject.logout();
}
注解配置文件方式
配置文件
@Configuration
public class ShiroConfig {
//Realm 认证逻辑
@Bean
public Realm getRealm(){
return new MyRealm();
}
//SecurityManager
@Bean
public DefaultWebSecurityManager securityManager(Realm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
//ShirFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
}
@Configuration
public class MyRealm extends AuthorizingRealm{
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {}
//验证用户信息
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取到当前用户的信息
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String username = usernamePasswordToken.getUsername();
//从数据库中拿取比对,这里默认 userName 张三 userPass 123,
UserBean userBean = new UserBean("张三",123);
//没有查到就没有这个用户
if(userBean == null){
//抛出UnknownAccountException
return null;
}
//返回AuthenticationToken,完成认证流程,无需接触密码敏感信息,让shiro帮我们做
SimpleAuthenticationInfo simpleAuthenticationInfo =
new SimpleAuthenticationInfo(userBean,
userBean.getUserPass(), "myRealm");
return simpleAuthenticationInfo;
}
}
@Test
public void testHelloworld() {
//1、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
//2、登录,即身份验证
subject.login(token);
} catch (AuthenticationException e) {
//5、身份验证失败
}
Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录
//3、退出
subject.logout();
}
从上述代码认证步骤可以了解到
- 首先通过
new IniSecurityManagerFactory并指定一个ini配置文件来创建一个SecurityManager;或者让spring帮我们创建,需要在配置类中进行配置。 - 通过
SecurityUtils得到Subject,其会自动绑定到当前线程;如果 web 环境在请求结束时需要解除绑定;然后获取身份验证的Token,如用户名 / 密码; - 调用
subject.login方法进行登录,其会自动委托给SecurityManager.login方法进行登录;如果身份验证失败请捕获AuthenticationException或其子类。 - 登录后shiro会自动通过配置文件或者通过realm中配置的认证方式进行认证。
- 最后可以调用
subject.logout退出,其会自动委托给SecurityManager.logout方法退出。
SecurityManager中的login方法调用subject的login进行登录,在SecurityManager的实现类
DefaultSecurityManager 中有具体实现
登录流程总结
- 首先调用
Subject.login(token)进行登录,其会自动委托给SecurityManager,调用之前必须通过SecurityUtils.setSecurityManager()设置。如果使用配置类需要进行配置; SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回 / 抛出异常表示身份验证成功了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问