ssm+shiro 实现无密码直接登录

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Honnyee/article/details/82895980

利用shiro可以十分方便,有效的实现接口权限控制功能。

但是在开发中,会遇到了这样一种场景:

在微信公众号开发里,用户在公众号里输入用户名和密码绑定项目,绑定完成后,下次进入项目跳过登录 直接进入项目的首页。在这个过程中,用户第二次登录时是使用微信提供的openId直接登录,这是免密登录的。

由于shiro登录是默认需要账号密码才能生成token,进而进行登录验证功能的,所以需要改写。

注意,此时应该是密码和免密同时存在的,而不是直接粗暴的去掉密码验证。

查找了网上的资料后处理方式有2种

第一种:重写token,验证时发现为免密类型时直接返回true

整体思路:

自定义token,加入免密标识符,在进行授权时判断是密码登陆或无密码登陆,自定义密码认证方法,即写一个方法继承HashedCredentialsMatcher,重写其中的doCredentialsMatch,将其中的token改写为自定义的token,最后将自己写的密码认证方法注入shiro

1.shiro的配置文件里面有一个是<bean id="credentialsMatcher"  class="xxxxx.xxxMatcher">这需要一个继承HashedCredentialsMatcher的子类,重写doCredentialsMatch方法

2.自定义一个token,继承自UsernamePasswordToken,添加一个标识符表名是否免密登录。

3.Subject subject = SecurityUtils.getSubject();
   EasyTypeToken token = new EasyTypeToken(loginName);
   subject.login(token);

4.回到第一步   重写方法的第一行  直接强转 token   获取标识符   如果为免密登录  直接返回true

--------来自https://blog.csdn.net/Sil_sunday/article/details/81986025?utm_source=copy

他在这篇文章里有实现代码,但是我在实验的过程中发现 AuthenticationToken不能直接转换成EasyTypeToken类型,博主可能写掉了 重写createToken部分,我自己尝试重写,但是始终报错类型转换失败,这种方式我还是没有实现。但是这个思路是可行的。

第二种:在免密登录时,在后台写一个默认的登录密码

由于第一种方法的问题迟迟没有解决,就觉得考虑其他方法,后来发现有一种非常简单的方法:

在免密登录的情况下,写一个固定的密码生成token,并将它的MD5加密后的内容放到 SimpleAuthenticationInfo里,这样就直接可以登录成功了。

实现代码如下:

这是reaml文件 doGetAuthenticationInfo方法的代码

 /**
     * Authentication(身份验证):简称为“登录/绑定”,即证明用户是谁。
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        // 1. 把AuthenticationToken转换为UsernamePasswordToken
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        // 2. 从UsernamePasswordToken中获取staffId
        String staffId = upToken.getUsername();
        //查看UsernamePasswordToken可知,getCredentials()方法的返回值是char []类型的,所以不能直接转化成string。
        char [] ch = (char[]) token.getCredentials();
        //接收输入的密码
        String password = new String(ch);

        // 3. 若用户不存在,抛出UnknownAccountException异常
        ShiroUser shiroUser = shiroUserService.selectByStaffId(staffId);
        if (shiroUser == null) {
            throw new UnknownAccountException("用户不存在!");
        }
        // 根据用户的情况,来构建AuthenticationInfo对象并返回,通常使用的实现类为SimpleAuthenticationInfo
        //判断是否是免密登录。
        // 在控制器里,正常的登录(绑定)逻辑是用户输入工号和密码,直接登录是直接把密码赋值为:无需密码直接登录
        if ("无需密码直接登录".equals(password)){
            //MD5 2次加密后
            String passwordMD5 = "fde9e13a5544700bf59ca24b11ffdc81";
            String realmName = getName();
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(staffId, passwordMD5, null,
                    realmName);
            return info;
        }else {
            // (2)credentials:密码
            Object credentials = shiroUser.getPassword();
            String realmName = getName();
            // (4)盐值:取用户信息中唯一的字段来生成盐值,避免由于两个用户原始密码相同,加密后的密码也相同,这里没有加盐
            //ByteSource credentialsSalt = ByteSource.Util.bytes(staffId);
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(staffId, credentials, null,
                    realmName);
            return info;
        }
    }

 这是控制器的写法

    @RequestMapping
    public void checkBind(HttpServletResponse response, HttpSession session) {
        try {
            String openId = "xxx";
            session.setAttribute("openId", openId);
            String staffId = userOpenIdService.selectStaffIdByOpenId(openId);

            //如果已绑定就跳到langya的首页
            if (staffId != null) {
                //如果已绑定 进行免密登录
                Subject subject = SecurityUtils.getSubject();
                String password = "无需密码直接登录";
                UsernamePasswordToken token = new UsernamePasswordToken(staffId, password);
                token.setRememberMe(false);
                subject.login(token);
                session.setAttribute("staffId", staffId);
               
            }
            //如果没有绑定就跳到绑定页
           

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

shiro配置文件里:

<!-- 6. 配置ShiroFilter -->
    <!-- 6.1 id必须和web.xml中配置的DelegatingFilterProxy的<filter-name>一致。 如果不一致,会抛出NoSuchBeanDefinitionException异常,因为shiro会在IOC容器中查找名称和<filter-name>
        值一致的filter bean -->
    <bean id="myAuthorizationFilter" class="com.wolwo.shiro.MyAuthorizationFilter"/>

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <!-- 配置哪些页面需要受保护,以及访问这些页面需要的权限 -->
        <property name="filters">
            <map>
                <!--<entry key="authc" value-ref="myLoginFilter"/>-->
                <entry key="myAuthorizationFilter" value-ref="myAuthorizationFilter"/>
            </map>
        </property>

        <property name="filterChainDefinitions">
            <value>
                <!-- 静态资源 -->
                /images/** = anon
                /js/** = anon
                /css/**= anon
                /res/** = anon
                /res/** = anon

                <!-- 第一次匹配优先的原则 -->
                /checkBind = anon
                /bind = anon
                /login = anon
                /user/login = anon
                /logout = logout
                <!--/** = authc-->
                /** = myAuthorizationFilter

            </value>
        </property>

这样,就可以很简单的实现免密登录功能了。

猜你喜欢

转载自blog.csdn.net/Honnyee/article/details/82895980
今日推荐