1. 程式人生 > >ssm+shiro 實現無密碼直接登入

ssm+shiro 實現無密碼直接登入

利用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>

這樣,就可以很簡單的實現免密登入功能了。