1.shiro认证
1.1.身份验证
身份验证:一般需要提供如身份ID等一些标识信息来表明登录者的身份,如提供email,用户名/密码来证明。
在shiro中,用户需要提供principals(身份)和credentials(证明)给shiro,从而应用能验证用户身份。
principals:身份,即主体的标识属性,可以是任何属性,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/邮箱/手机号。
credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
最常见的principals和credentials组合就是用户名/密码。
1.2.shiro认证思路分析
(1)获取当前的Subject,调用SecurityUtils.getSubject();
(2)测试当前的用户是否已经被认证,即是否已经登录。调用Subject的isAuthenticated();
(3)若没有被认证,则把用户名和密码封装为UsernamePasswordToken对象。
①创建一个表单页面
②把请求提交到SpringMVC的Handler
③获取用户名和密码
(4)执行登录:调用Subject的login(AuthenticationToken)方法。
(5)自定义Realm的方法,从数据库中获取对应的记录,返回给Shiro。
①实际上需要继承org.apache.shiro.realm.AuthenticatingRealm类
②实现doGetAuthenticationInfo(AuthenticationToken)方法
(6)由shiro完成对密码的比对。
1.3.实现认证的相应代码
这部分包含了MD5加密、MD5盐值加密、多realms、认证策略
(1)编写相应的login.jsp、list.jsp页面。
(2)controller
@Controller
@RequestMapping("/shiro")
public class ShiroController {
@RequestMapping("/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password){
Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated()) {
// 把用户名和密码封装为 UsernamePasswordToken对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token.setRememberMe(true);
try {
currentUser.login(token);
}
catch (AuthenticationException ae) {
System.out.println("登录失败: " + ae.getMessage());
}
}
return "redirect:/list.jsp";
}
}
(3)第一个realm,ShiroRealm.java,密码采用MD5盐值加密。盐值需要唯一:一般使用随机字符串或 user id。
public class ShiroRealm extends AuthenticatingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
System.out.println("[FirstRealm] doGetAuthenticationInfo");
//1. 把 AuthenticationToken 转换为 UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2. 从 UsernamePasswordToken 中来获取 username
String username = upToken.getUsername();
//3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
System.out.println("从数据库中获取 username: " + username + " 所对应的用户信息.");
//4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
if("unknown".equals(username)){
throw new UnknownAccountException("用户不存在!");
}
//5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常.
if("monster".equals(username)){
throw new LockedAccountException("用户被锁定");
}
//6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
//以下信息是从数据库中获取的.
//1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.
Object principal = username;
//2). credentials: 密码.
Object credentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";
if("admin".equals(username)){
credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
}else if("user".equals(username)){
credentials = "098d2c478e9c11555ce2823231e02ec1";
}
//3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
String realmName = getName();
//4). 盐值.
ByteSource credential