SpringBoot整合shiro(一):登录流程
shiro登录
博客只讲思路
具体的代码 直接去github拿就可以
不出意外的话 配置一下mysql redis host 密码之类的 应该是可以拿了就能用的
不能的话 我再改改*- *
大概的思路是这个样子
image.png
使用框架第一步之记单词qwq
当然 记不住 也一样用*- *
Authentication n. 认证;鉴定 (用来登陆)
Authorization n. 授权;批准;批准书;授权书(用来鉴权)
Principal n. 本金;委托人;资本;主角(用户名等用户信息)
Credential n. 凭证;国书(密码等验证信息)
credentialsMatcher 凭证校验
authenticated v. 认证;鉴定;为…出立证据 (已经通过认证)
Subject n. 学科;主题;问题;话题(shiro里面这个就是登陆者本人 你可以通过这个进行各种操作)
Realm n. 领域;场所;王国(shiro核心类 核心和数据交互的部分)
登录入口
controller
只是一个简单的用来测试的登录入口
image.png
shiro是支持记住密码功能的 但是代码还有点bug //todo
登录核心的就是tokenService.login
shiro提供了各种异常来给用户进行try catch的方式 返回结果(当然你可以对他们进行重写 重新定义)
注:这边是通过邮箱来作为用户名登录的
/**
* 登陆
*
* @param user 登陆用户信息 (用email 和 password 登陆)
* @param rememberMe 是否记住密码(默认记住)
* @return 登入结果
*/
@GetMapping("/login")
public String login(SysUser user, @RequestParam(required = false,defaultValue ="true")String rememberMe) {
if (StringUtils.isEmpty(user.getEmail()) || StringUtils.isEmpty(user.getPassword())) {
return "请输入用户名和密码!";
}
//try catch 封装到了aop中
//进行验证,这里可以捕获异常,然后返回对应信息
Boolean isRememberMe ="true".equals(rememberMe);
tokenService.login(user, isRememberMe);
subject.checkRole("admin");
subject.checkPermissions("query", "add");
return "login success!";
}
shiro核心登陆方法
SecurityUtils.getSubject().login(shiroToken)
shiro有几个自己定义的核心概念 token就是用来作为登陆的令牌
自定义的token
我觉得shiro之所以是个很优秀的框架就是
他提供了大量的核心组件 让你重写
可以在token里面封装大量的业务数据 来自己拼装想要的实现的业务需求
后面也可以看到大量的地方 提供了方便重写的地方
/**
*
* 自定义 封装的shiro框架使用的token
*
* 父类属性
* **@param private String username;
* **@param private char[] password;
* **@param private boolean rememberMe;
* **@param private String host;
*
* @author zxc
* @since 2020/12/7.
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class MyShiroToken extends UsernamePasswordToken implements Serializable {
private static final long serialVersionUID = -7767370043611561238L;
private String stringPassword;
private String tokenCustomAttribute1;
private String tokenCustomAttribute2;
private String tokenCustomAttribute3;
private String tokenCustomAttribute4;
public void setStringPassword(String stringPassword) {
this.stringPassword = String.valueOf(super.getPassword());
}
/**
* 因为父类的password是char类型 这里强行写成string 避免麻烦
*/
public String getStringPassword() {
return String.valueOf(this.getPassword());
}
public MyShiroToken(String email, String password) {
super(email,password);
}
public MyShiroToken(String username, String password, boolean aBoolean, String host, String tokenCustomAttribute1, String tokenCustomAttribute2) {
super(username,password,aBoolean,host);
this.tokenCustomAttribute1=tokenCustomAttribute1;
this.tokenCustomAttribute2=tokenCustomAttribute2;
}
}
这里把用户user的各种信息 封装到token里面 交给shiro管理 以后你的每次访问 shiro都知道你的令牌在那 给你放行
image.png
在这个地方我自定义了MyShiroToken 可以根据自己的业务需求 封装进自己想要使用的参数 然后后面去校验
/**
* 用户登陆
*
* @param user 用户信息
* @param rememberMe 记住密码
* @return 用户信息
*/
@Override
public SysUser login(SysUser user, Boolean rememberMe) {
//构造一个登录使用的集中用户数据的token
MyShiroToken shiroToken = new MyShiroToken(user.getEmail(), user.getPassword(),rememberMe,"host","token1","token2");
shiroToken.setRememberMe(rememberMe);
//使用token进行登陆
SecurityUtils.getSubject().login(shiroToken);
return getToken();
}
shiro核心组件realm(领域)
shiro最主要的登录和权限校验的中转站
shiro最为重要的功能汇集点都在这个地方
注:展示的部分 只是登录部分
每次登陆 我们都会走到这个doGetAuthenticationInfo登陆校验的方法来
他首先会拿到刚刚登陆使用的token 楼上的login(shiroToken)
然后shiro就会根据你的信息 去核实数据库中的数据 确定你是不是满足
这边又引入了一个新的核心组件AuthenticationInfo
AuthenticationInfo就是封装好的一个 用户信息 (当然你可以在里面封装任何东西)
通常了 我们一般把从数据库查询的该用户真实数据信息(主要是密码)
封装到info中 然后交给下一位流水线工人 去校验你的密码
/**
* @author zxc
* @Desciption: shiro核心登录鉴权类
* @Auther: ZhangXueCheng4441
* @Date:2020/12/2/002 20:48
*/
public class MyRealm extends AuthorizingRealm {
@Resource
private SysUserService sysUserService;
@Resource
private SysUserDao sysUserDao;
/**
* @param authenticationToken 登入用户信息
* @return 鉴定信息 到match校验密码
* @throws AuthenticationException 登录异常
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//接受到shiro管理的用户token (配置redis 解决类型转换异常问题)
MyShiroToken loginToken = (MyShiroToken) authenticationToken;
if (StringUtils.isEmpty(authenticationToken.getPrincipal())) {
return null;
}
//获取用户信息
Object principal = authenticationToken.getPrincipal();
LambdaQueryWrapper<SysUser> lambdaWrapper = new QueryWrapper<SysUser>()
.lambda()
.eq(SysUser::getEmail, loginToken.getUsername());
SysUser dateBaseUserInfo = sysUserService.getOne(lambdaWrapper);
if (dateBaseUserInfo == null) {
//这里返回后会报出对应异常
return null;
} else {
//这里验证弹去match 校验密码 这里把该用户真实密码封装到info
//想怎么封装怎么封装 看业务需要
return new MyAuthenticationInfo(dateBaseUserInfo, "password", getName());
}
}
}
ps:(MyShiroToken) authenticationToken;这个部分在没有整合redis之前 会有很麻烦的问题
需要重写token的创建方法 7788的 很麻烦
自定义AuthenticationInfo
可以在里面根据自己的业务需求
封装任何数据进去
因为下一步就是要去校验密码了 所以可以在里面根据业务需求封装一些特殊的标识啊
比如uuid啊 雪花算法生成的唯一标识 时区 等等
然后按自己的想法进行密码校验
/**
* 父类属性
* PrincipalCollection principals; getPrimaryPrincipal 可以获得用户信息
* Object credentials;
* ByteSource credentialsSalt;
*
* @Desciption: 自定义 封装的shiro框架使用的验证信息类
* @author zxc
* @since 2020/12/7
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class MyAuthenticationInfo extends SimpleAuthenticationInfo {
private static final long serialVersionUID = 812808092055322168L;
private String infoCustomAttribute1;
private String infoCustomAttribute2;
public MyAuthenticationInfo(SysUser dateBaseUserInfo, String password, String name) {
super(dateBaseUserInfo,password,name);
}
}
密码校验
当然得到了用户的信息(token)和数据库中用户的正确信息(info)
image.png
我们就可以进行密码校验啦
当然
我这里是写的最简单的对比密码和用户输入的密码
正常开发密码必然是加密的
可以选择MD5加密啊 token方式 各种方式来设计你想要的的密码校验方法
当然前端的小姐姐也可以添加自己对输入密码的加密 编码
商量好就可以实现很复杂的校验了
/**
*
* @author zxc
* @Desciption: 密码校验类 (需要配置交给shiro)
* @Auther: ZhangXueCheng4441
* @Date: 2020/12/4/20:44
*/
@Component
public class MyPasswordMatcher implements CredentialsMatcher {
/**
* 重写shiro密码校验
*
* @param authenticationToken 用户登录信息token(已经封装成 MyShiroToken)
* @param authenticationInfo 该用户名的user的验证信息 (已经封装成 MyAuthenticationInfo)
* @return 加密之后的密码验证
*/
@Override
public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
boolean matches = false;
//类型转换异常 配置redis缓存 解决
//登陆的用户信息 token
MyShiroToken token = (MyShiroToken) authenticationToken;
String loginUserPassword = token.getStringPassword();
//数据库用户信息 token
MyAuthenticationInfo dataBaseInfo = (MyAuthenticationInfo) authenticationInfo;
SysUser dataBaseUser = (SysUser) dataBaseInfo.getPrincipals().getPrimaryPrincipal();
//对比两个密码
if (dataBaseUser.getPassword().equals(loginUserPassword)) {
matches = true;
}
return matches;
}
}
登出
因为配置了redis
登出当然要清除redis中的session 服务器中shiro保存的信息 等等
shiro在底层都给我们实现了
image.png
触发SecurityUtils.getSubject().logout()这个方法即可
/**
* 登出
*/
@Override
public void logout() {
//清除缓存
//customRealm.doClearCache(SecurityUtils.getSubject().getPrincipals());
SecurityUtils.getSubject().logout();
}
copy地址
https://github.com/opop32165455/zeroBeginning.git
里面的zero_shiro模块
其他的模块也在龟速进展中
欢迎copy 我会一直改进我的代码的 有啥问题 issues讨论一哈
后面权限校验 配置思路 源码学习 都会写进来的啦
别的我不敢当 但是写注解 那我肯定是最详细的
有什么问题欢迎辱骂我