美文网首页
SpringBoot整合shiro(一):登录流程

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

作者: 圆企鹅i | 来源:发表于2021-01-05 21:43 被阅读0次

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

    相关文章

      网友评论

          本文标题:SpringBoot整合shiro(一):登录流程

          本文链接:https://www.haomeiwen.com/subject/npwwoktx.html