SpringBoot整合shiro(一):登录流程

2021-01-05  本文已影响0人  圆企鹅i

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就是用来作为登陆的令牌

image.png

自定义的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

image.png

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讨论一哈
后面权限校验 配置思路 源码学习 都会写进来的啦
别的我不敢当 但是写注解 那我肯定是最详细的
有什么问题欢迎辱骂我

上一篇下一篇

猜你喜欢

热点阅读