项目代码来自:
12-Shiro与Springboot整合_哔哩哔哩_bilibili
关注尚硅谷微信公众号 ,输入shiro系统返回下载链接(百度网盘链接中含代码和讲义):
数据库建表语句在讲义中,需自行添加测试用户数据。
项目仅适合学习测试,设置两个断点:
1、../controller/MyController.java中的44行左右subject.login(token);
/* com/atguigu/shiro/controller/MyController.java 文件 */
@Controller
@RequestMapping("myController")
public class MyController {
@GetMapping("userLogin")
@ResponseBody
public String userLogin(String name, String pwd,
@RequestParam(defaultValue = "false")boolean rememberMe,
HttpSession session){
try {
subject.login(token); //断点1
//return "登录成功";
session.setAttribute("user",token.getPrincipal().toString());
return "main";
} catch (AuthenticationException e) {
e.printStackTrace();
System.out.println("登录失败");
return "登录失败";
}
}
2、../realm/MyRealm.java中第46行以后的doGetAuthenticationInfo()方法内
/* com/atguigu/shiro/realm/MyRealm.java 中的doGetAuthenticationInfo()方法内*/
@Component
public class MyRealm extends AuthorizingRealm {
//自定义登录认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1获取用户身份信息
String name = authenticationToken.getPrincipal().toString(); //断点2
//2调用业务层获取用户信息(数据库)
User user = userService.getUserInfoByName(name);
//3非空判断,将数据封装返回
if(user !=null){
AuthenticationInfo info = new SimpleAuthenticationInfo(
authenticationToken.getPrincipal(),
user.getPwd(),
ByteSource.Util.bytes("salt"),
authenticationToken.getPrincipal().toString()
);
return info;
}
return null;
}
}
debug运行,调用接口http://localhost:8080/myController/userLogin?name=张山&pwd=123456
程序运行过程依次经过:
1、com.atguigu.shiro.controller.MyController#userLogin(java.lang.String, java.lang.String, boolean, javax.servlet.http.HttpSession),携带参数token
2、org.apache.shiro.subject.support.DelegatingSubject#login,携带参数token
3、org.apache.shiro.mgt.DefaultSecurityManager#login,携带参数token
4、org.apache.shiro.mgt.AuthenticatingSecurityManager#authenticate,携带参数token
5、org.apache.shiro.authc.AbstractAuthenticator#authenticate,携带参数token
6、org.apache.shiro.authc.pam.ModularRealmAuthenticator#doAuthenticate,传给下一级的参数有2个,分别是Realm对象和authenticationToken。
Realm对象是我们自定义的com.atguigu.shiro.realm.MyRealm
7、org.apache.shiro.authc.pam.ModularRealmAuthenticator#doSingleRealmAuthentication,到这里调用realm.getAuthenticationInfo(token),才开始验证token中的用户名密码是否合法(一般是与数据库中的用户名密码进行比对,比对一致为合法)
8、org.apache.shiro.realm.AuthenticatingRealm#getAuthenticationInfo
9、org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo
具体实现为:com.atguigu.shiro.realm.MyRealm#doGetAuthenticationInfo
项目com.atguigu.shiro.config.ShiroConfig配置中的Bean对象defaultWebSecurityManager()从继承关系图谱上看,自定义的DefaultWebSecurityManager是第4条AuthenticatingSecurityManager的子类。
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//1创建defaultWebSecurityManager 对象
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//2创建加密对象,设置相关属性
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//2.1采用md5加密
matcher.setHashAlgorithmName("md5");
//2.2迭代加密次数
matcher.setHashIterations(3);
//3将加密对象存储到myRealm中
myRealm.setCredentialsMatcher(matcher);
//4将myRealm存入defaultWebSecurityManager 对象
defaultWebSecurityManager.setRealm(myRealm);
//4.5设置rememberMe
defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
//4.6设置缓存管理器
defaultWebSecurityManager.setCacheManager(getEhCacheManager());
//5返回
return defaultWebSecurityManager;
}
在配置中使用的3个方法:setRealm()、setRememberMeManager()、setCacheManager()都在父类中有定义。 //4将myRealm存入defaultWebSecurityManager 对象 defaultWebSecurityManager.setRealm(myRealm); //4.5设置rememberMe defaultWebSecurityManager.setRememberMeManager(rememberMeManager()); //4.6设置缓存管理器 defaultWebSecurityManager.setCacheManager(getEhCacheManager());
所以在shiro验证用户名密码流程中,调用 AuthenticatingSecurityManager#authenticate时,shiro才能根据继承关系,找到实现对象defaultWebSecurityManager以及其配置的realm对象myRealm,执行myRealm的方法。