shiro学习二
认证:
1. 认证流程:
-
获取当前的 Subject. 调用 SecurityUtils.getSubject();
-
测试当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated()
-
若没有被认证, 则把用户名和密码封装为 UsernamePasswordToken 对象
-
执行登录: 调用 Subject 的 login(AuthenticationToken) 方法.
-
自定义 Realm 的方法, 从数据库中获取对应的记录, 返回给 Shiro.
1). 实际上需要继承 org.apache.shiro.realm.AuthenticatingRealm 类
2). 实现 doGetAuthenticationInfo(AuthenticationToken) 方法. -
由 shiro 完成对密码的比对.
2.代码
login.jsp页面代码 ,用于接收用户名和密码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="shiro/login" method="POST">
username: <input type="text" name="username"/>
<br><br>
password: <input type="password" name="password"/>
<br><br>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
controller 代码
@Controller
@RequestMapping("/shiro")
public class shiroLogin {
@RequestMapping("/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password){
//获得当前用户的subject
Subject currentUser = SecurityUtils.getSubject();
//判断当前用户是否被授权
if (!currentUser.isAuthenticated()) {
// 把用户名和密码封装为 UsernamePasswordToken 对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
// 执行登录. 将token 传到 doGetAuthenticationInfo
currentUser.login(token);
}
// ... catch more exceptions here (maybe custom ones specific to your application?
// 所有认证时异常的父类.
catch (AuthenticationException ae) {
//unexpected condition? error?
System.out.println("登录失败: " + ae.getMessage());
}
}
//邓露成功后重定向到list.jsp
return "redirect:/list.jsp";
}
}
自定义Realm代码
上次我们自定义的realm类仅仅实现了Realm接口,如果我们要做认证,那么我们需要继承AuthenticatingRealm类,并重写doGetAuthenticationInfo方法。执行subject.login(token)方法后,就会来到 doGetAuthenticationInfo()里。
public class CustomRealm extends AuthenticatingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("进入CustomRealm");
//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 =123 ;
//3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
String realmName = getName();
// shiro 为我们做一个密码的比对 credentials 和 upToken (中的密码)进行比对
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
return info;
}
}
由于shiroFilter会拦截from表单的提交请求,所以我们要在shiroFilter对登录请求放开,让其可以匿名访问
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
....
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/shiro/login", "anon");
//用户退出
filterChainDefinitionMap.put("/shiro/logout", "logout");
}
list.jsp 用户登录成功后的页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
list page
<!--用户退出 ,用户登录后,不进行退出操作 ,回退到登录界面,这时密码不正确也会登录成功,所以当用户正常退出,需要有一个登出请求 -->
<a href="shiro/logout">logout</a>
<!-- 登出请求被shiroFilter拦截到,然后匹配到url,过滤器名称位logout,shiro位会为我们做用户退出-->
</body>
</html>
测试:
来到login.jsp页面
用户名输入 unknown 或 monster 会被捕获异常 ,密码不是123 ,登录也会失败,重定向到login.jsp,可以查看控制台的信息。
器名称位logout,shiro位会为我们做用户退出–>
```测试:
来到login.jsp页面
用户名输入 unknown 或 monster 会被捕获异常 ,密码不是123 ,登录也会失败,重定向到login.jsp,可以查看控制台的信息。