Version: Next

Shiro实现用户认证

  1. 登录成功,则前往index页面

  2. 登录失败,输出失败原因

Controller计算Token实现登录

  • 新建登录路由/user/login
    • 使用SecurityUtils获取当前用户
    • 使用UsernamePasswordToken的构造方法,传入username和password来生成Token
    • Token登录,会进入UserRealm的认证方法
@RequestMapping("/user/login")
public String login(HttpServletRequest request, Model model) {
String username = request.getParameter("username");
String password = request.getParameter("password");
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//拿着token登录
try {
//会进入UserRealm的认证方法
currentUser.login(token);
return "index";
} catch (UnknownAccountException e) { //用户名不存在
model.addAttribute("msg", "用户名不存在");
return "user/login";
} catch (IncorrectCredentialsException e) {//密码错误
model.addAttribute("msg", "密码错误");
return "user/login";
}
}
  • 登录页面
    • 添加表单提交路由地址
    • 如果登陆失败,显示失败原因
<body>
<h1>登录</h1><br>
<#-- 只有msg存在时,才显示msg内容-->
<#if msg??><p style="color: red">${msg}</p> </#if>
<form action="/user/login" method="post">
<p> 用户名:<input type="text" name="username"/></p>
<p> 密码:<input type="password" name="password"/></p>
<p><input type="submit" value="登录"></p>
</form>
</body>

自定义Realm查询用户名密码

  • 获取Controller计算好的token,

    • 从token获取用户名、密码
  • 查询数据库获取用户信息

    • 查询用户名、密码
  • 认证

    • 调用token.getUsername认证用户名,失败return null,自动抛出UnknownAccountException异常

    • 密码认证Shiro自动完成

    • 返回类型AuthenticationInfo是一个接口,我们返回它的实现类SimpleAuthenticationInfo

    • Simple中可以传四个参数也可以传三个参数。三个参数省略

      • 第一个参数,有的人传的是userInfo对象对用的用户名。在学习过程中,传入的都是user对象,没有尝试过对象对应的用户名,但是从前辈们的经验看得到,此处也可以传用户名,因人而异吧。

      • 第二个参数,传的是从数据库中获取的password,然后再与token中的password进行对比,匹配上了就通过,匹配不上就报异常。

      • 第三个参数–用于加密密码对比,–获取的经验:为了防止两用户的初始密码是一样的, –巨佬们的解答:四个参数,防止两用户可能初始密码相同时候用,token 用simplehash(四个参数的构造) 加密默认用了MD5 迭代一次加密,info中在密码比对调用new SimpleHash(String algorithmName, Object source)这个实例化对象默认迭代一次了,所以当你用三个参数加密时候可能两 个初始密码相同人的就没能区别开 (因此realm中密码要从数据库的查的原因),通过设置reaml 中credentialsMatcher 属性的各项属性可实现

      • 第四个参数:当前realm的名字。

      —ps:第一个参数可以直接通过token.getPrincipal()方法获取—获取当前记录的用户名,从这个用户名获取一系列的对应需求属性。

    public class UserRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    System.out.println("执行了<<授权>>方法");
    return null;
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    System.out.println("执行了<<认证>>方法");
    //查询数据库获得用户数据
    String username = "root";
    String password = "123456";
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    if (!token.getUsername().equals(username)) {
    return null; //Shiro自动抛出 UnknownAccountException
    }
    //密码认证,shiro会自动做
    return new SimpleAuthenticationInfo("", password, "");
    }
    }

测试

  • index.ftl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
首页<br>
<#if msg??><p style="color:red">${msg}</p></#if>
<hr>
<a href="/user/add">add</a> | <a href="/user/update">update</a>
</body>
</html>