一、shiro认证流程
- 获取当前的 Subject. 调用 SecurityUtils.getSubject();
- 测试当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated()
- 若没有被认证, 则把用户名和密码封装为 UsernamePasswordToken 对象
1). 创建一个表单页面
2). 把请求提交到 SpringMVC 的 Handler
3). 获取用户名和密码. - 执行登录: 调用 Subject 的 login(AuthenticationToken) 方法.
@RequestMapping(value = "login",method = RequestMethod.POST)
public String login(@RequestParam("username") String username,@RequestParam("password") String password){
//获取当前subject
Subject subject = SecurityUtils.getSubject();
//判断是否有有效凭据来证明了自己的身份,如果没有则进行登录认证
if(!subject.isAuthenticated()){
//1、获取提交的用户名以及密码封装为UsernamePasswordToken
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
try{
//登录认证
subject.login(usernamePasswordToken);
}catch (AuthenticationException ae){ /* 抛出的异常全为AuthenticationException的子类 */
System.out.println("登陆失败:" + ae.toString());
}
}
return "redirect:list";
}
- 自定义 Realm 的方法, 从数据库中获取对应的记录, 返回给 Shiro.
1). 实际上需要继承 org.apache.shiro.realm.AuthenticatingRealm 类
2). 实现 doGetAuthenticationInfo(AuthenticationToken) 方法. - 由 shiro 完成对密码的比对.
/**
* 1. 授权需要继承 AuthorizingRealm 类, 并实现其 doGetAuthorizationInfo 方法
* 2. AuthorizingRealm 类继承自 AuthenticatingRealm, 但没有实现 AuthenticatingRealm 中的
* doGetAuthenticationInfo, 所以认证和授权只需要继承 AuthorizingRealm 就可以了. 同时实现他的两个抽象方法.
*/
public class MyRealm extends AuthorizingRealm {
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//将AuthenticationToken强转
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//通过UsernamePasswordToken获取用户名以及密码的信息
String username = token.getUsername();
char[] password = token.getPassword();
//判断查询用户情况,判断是否有改用户
System.out.println("从数据库获取用户数据");
//判断用户情况,抛出异常
//用户不存在,抛出异常
if ("unknown".equals(username)){
throw new UnknownAccountException("用户不存在");
}
//如果用户被锁定,抛出异常
if ("monster".equals(username)){
throw new LockedAccountException("用户被锁定");
}
//6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
//以下信息是从数据库中获取的.
//1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.
Object principal = username;
//2). credentials: 密码.(加密方式在配置文件中通过credentialsMatcher属性配置)
Object credentials = "e10adc3949ba59abbe56e057f20f883e"; /* 123456的加密结果 MD5加密1次 */
//3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
String realmName = getName();
//4). 盐值.(防止用户同密码的状况)
//通过该方法获取盐值(传入的属性必须是唯一的)
// ByteSource credentialsSalt = ByteSource.Util.bytes(username);
//输入的用户名、密码加密后的值、盐值必须和传入的值一样,才可获取认证
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
// info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
return info;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
二、加密
- 在配置文件中配置加密方式
替换当前 Realm 的 credentialsMatcher 属性. 直接使用 HashedCredentialsMatcher 对象, 并设置加密算法即可.
<!--
配置 Realm
直接配置实现了 org.apache.shiro.realm.Realm 接口的 bean
-->
<bean id="jdbcRealm" class="com.shiro.test.realm.MyRealm">
<!-- 通过配置credentialsMatcher属性来,替换realm的加密算法 -->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 指定加密方式 -->
<property name="hashAlgorithmName" value="MD5"></property>
<!-- 指定加密次数 -->
<property name="hashIterations" value="1"></property>
</bean>
</property>
</bean>
- 通过使用SimpleAuthenticationInfo来让shiro认证
//通过该方法获取盐值(传入的属性必须是唯一的)
ByteSource.Util.bytes(username);
//传入的用户和密码均为数据库中获得
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
//盐值加密
// info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
- 可以通过通过使用SimpleHash获取密码加密结果
@Test
public void test02(){
String hashAlgorithmName = "MD5";
Object credentials = "123456";
ByteSource salt = ByteSource.Util.bytes("admin");
int hashIterations = 1;
Object simpleHash = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
Object simpleHash1 = new SimpleHash(hashAlgorithmName, credentials);
System.out.println(simpleHash1);
}