认证流程:
1. 获取当前的 Subject. 调用 SecurityUtils.getSubject();
2. 测试当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated() 3. 若没有被认证, 则把用户名和密码封装为 UsernamePasswordToken 对象1). 创建一个表单页面2). 把请求提交到 SpringMVC 的 Handler3). 获取用户名和密码. 4. 执行登录: 调用 Subject 的 login(AuthenticationToken) 方法. 5. 自定义 Realm 的方法, 从数据库中获取对应的记录, 返回给 Shiro.1). 实际上需要继承 org.apache.shiro.realm.AuthenticatingRealm 类2). 实现 doGetAuthenticationInfo(AuthenticationToken) 方法. 6. 由 shiro 完成对密码的比对.
实现过程:
创建一个login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>Insert title here Login Page
登录成功后的list.jsp 包含登出按钮
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>Insert title here List Page
Logout
创建一个Controller ShiroHandler.java
package com.java.shiro.realms;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.subject.Subject;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;@Controller@RequestMapping("/shiro")public class ShiroHandler { @RequestMapping("/login") public String login(@RequestParam("userName") String userName, @RequestParam("password") String password) { Subject currentUser = SecurityUtils.getSubject(); if (!currentUser.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken(userName, password); token.setRememberMe(true); try { currentUser.login(token); } catch (AuthenticationException e) { System.out.println("登录失败:" + e.getMessage()); } } return "redirect:/list.jsp"; }}
创建一个Realm 重写AuthenticatingRealm 的doGetAuthenticationInfo()
package com.java.shiro.realms;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.LockedAccountException;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.authc.UnknownAccountException;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.realm.AuthenticatingRealm;public class ShiroRealm extends AuthenticatingRealm { @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { System.out.println("doGetAuthenticationInfo " + token); // 1. 把AuthenticationToken 转换为UsernamePasswordToken UsernamePasswordToken up = (UsernamePasswordToken) token; // 2. 从UsernamePasswordToken 中来获取username String username = up.getUsername(); // 3. 调用数据库的方法,从数据库中查询username对应的用户记录 System.out.println("从数据库中获取userName :" + username + " 所对应的用户信息."); // 4. 若用户不存在,则可以抛出 UnknownAccoountException 异常 if ("unknown".equals(username)) { throw new UnknownAccountException("用户不存在"); } // 5. 根据用户信息的情况,决定是否需要抛出其他的AuthencationException 异常 假设用户被锁定 if ("monster".equals(username)) { throw new LockedAccountException("用户被锁定"); } // 6. 根据用户的情况,来构建AuthenticationInfo 对象并返回,通常使用的是 // SimpleAuthenticationInfo // 以下信息是从数据库获取的. Object principal = username; // principal 认证的实体信息. // 可以是username,也可以是数据表对应的用户的实体类对象 String credentials = "123456"; // credentials:密码 String realmName = getName(); AuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName); return info; }}
在applicationContext.xml 中添加对应的 过滤器
/login.jsp= anon /shiro/login= anon /shiro/logout = logout # everything else requires authentication: /** = authc
在登录成功后,点击后退,再次随便输入依旧能够登录成果,是由于缓存的作用。
密码的比对:
通过AuthenticatingRealm 的 credentialsMatcher 属性进行的比对
credentialsMatcher 非常重要,因为在生产中数据库存的密码不会是一个明文的密码,会是一个加密后的密码,比对加密的密码也是通过credentialsMatcher 来进行比对