秒極啊!手把手帶你進行shiro受權攔截器的重寫,學到了學到了

shiro整合先後端分離的springboots,Vue項目真的是有不少大坑啊。

今天個人主題是:如何設置shiro過濾器。

遇到問題:個人項目是先後端分離的,shiro裏面有一個shiroFilterFactoryBean.setUnauthorizedUrl(「你本身的url」);
函數
html

這是什麼意思呢:這表示若是你訪問了一個須要權限的url,可是目前你登錄的角色沒有權限,那麼頁面默認跳轉的地址。
看着彷佛是沒啥毛病。
可是!!!
若是是先後端分離怎麼辦呢?後端shiro讓項目跳向前端某個頁面,這裏是先後端分離式的啊!當前端的用戶(沒權限)發送了一個請求被shiro直接攔截了,前端一臉懵不知道發生什麼問題了,因此傻傻的在控制檯返回一段紅色的跨域錯誤信息。
那麼咱們應該怎麼解決呢?
你確定會想那就讓後端讓前端跳轉啊,這裏我要說,既然是先後端分離的話,後端只能給前端發送信息讓前端根據返回的信息自行處理。
可是問題又來了,shiro這玩意直接把前端請求攔截了啥都不返回,啥都不說一聲,搞的前端還誤解是跨域問題。前端

那麼咱們就應該去改寫shiro的攔截器,讓它處理方式更人性化。vue

不廢話了,開始主題

開始改寫攔截器:
java

//package com.igeekhome.ccs.tool.config;  //寫本身的當前目錄

import lombok.SneakyThrows;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.springframework.boot.configurationprocessor.json.JSONObject;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

public class MyPermsFilter extends AuthorizationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
            throws Exception {
        System.out.println("isAccessDenied");
        Subject subject = getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;
        System.out.println("subject:"+subject.getPrincipal());
        if (rolesArray == null || rolesArray.length == 0) {
            //no roles specified, so nothing to check - allow access.
            return true;
        }

        for(int i=0;i<rolesArray.length;i++){
            if(subject.isPermitted(rolesArray[i])){
                System.out.println("rolealist:"+rolesArray[i]);
                return true;
            }
        }
        return false;
    }

    @SneakyThrows
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        System.out.println("onAccessDenied");
        Subject subject = getSubject(request, response);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        // If the subject isn't identified, redirect to login URL
        if (subject.getPrincipal() == null) {
//            saveRequestAndRedirectToLogin(request, response);     //沒登錄就進入從新登錄接口,這裏先後端分離不須要
            System.out.println("沒登錄");
            String objectStr= "{'name':'沒登錄'}";
            JSONObject jsonObject=new JSONObject(objectStr);
            PrintWriter wirte = null;
            wirte = response.getWriter();
            wirte.print(jsonObject);
        } else {
            System.out.println("沒權限");
            String objectStr= "{'name':'沒權限'}";
            JSONObject jsonObject=new JSONObject(objectStr);
            PrintWriter wirte = null;
            wirte = response.getWriter();
            wirte.print(jsonObject);
//            JSONObject json = new JSONObject();
//            json.put("state","403");
//            json.put("msg","登陸已失效,請從新登陸!");
//            out.println(json);
//            out.flush();
//            out.close();
        }

        return false;
    }
}

首先看到web

protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)

也就是第一個函數
解釋一下:這個函數每次shiro啓動都會運行,看到參數ServletRequest request, ServletResponse response這兩個參數是爲當前用戶身份確認起做用的,再看到Object mappedValue,它裏面存在你以前shiro配置文件裏存入的權限要求:
面試

我這裏存了一個:算法

filterChainDefinitionMap.put("/detail/getclass","perms[user:teacher]");

因此mappedValue裏存的應該是user:teacher,固然前提是把它轉化爲String
spring

再看到代碼:
數據庫

這是看看你以前有沒有存權限,若是沒存的話(列表大小爲0或列表爲空)返回true,shiro就不攔你了。
再往下看:
apache

這裏subject是什麼?你往上看找到:

這個就是獲取你當前登錄的用戶。
而後注意了!

if(subject.isPermitted(rolesArray[i])){
                System.out.println("rolealist:"+rolesArray[i]);
                return true;
            }

這是幹嗎,這就是拿當前用戶判斷isPermitted(),當前用戶有沒有perms的內容,也就是判斷subject裏面有沒有」user:teacher」權限,而後這裏有個大坑,網上基本上全部教程代碼是這樣的:

他們是hasRole,其實也沒毛病,可是我以前設置的是

perms因此要用isPermitted,你要發清楚你加的是什麼權限。
接着下一步:
若是找到了返回true,沒找到從for循環出來,直接返回false。
false出現了,怎麼辦呢?這時候第二個函數

protected boolean onAccessDenied(ServletRequest request, ServletResponse response)

知道你返回是false的話它就會運行,看紅色框內

這表明什麼意思,固然是獲取當前用戶的username了也就是標識碼了,這裏不懂得說明shiro你仍是沒弄懂()由於以前的自定義Realm類用過了。
若是subject.getPrincipal() == null說明沒有用戶名,意味着你尚未登錄呢,之前代碼這裏是一個跳轉函數(讓前端跳到登錄頁面,可是我是先後端分離因此沒用!),這裏我改爲了

String objectStr= "{'name':'沒登錄'}";
            JSONObject jsonObject=new JSONObject(objectStr);
            PrintWriter wirte = null;
            wirte = response.getWriter();
            wirte.print(jsonObject);

這是把自定義的objectStr 字符串轉化爲jsob格式,而後用PrintWriter返回給前端,這樣一旦出錯那麼shiro除了攔截請求外還會給前端發一個 "{‘name’:‘沒登錄’}「的json信息,這些前端就不懵了就不會說什麼跨域問題了。而是獲得了json數據。而後下面的else部份內容就不說了,和上面幾乎同樣給前端發送一個」{‘name’:‘沒權限’}"的信息,這意味着你權限出錯了,而且用戶也登錄了,那麼就只有一個可能致使第一個函數返回false,那就是你原本就沒有權限!

好了好了說了一大堆。

老規矩,給那些趕時間的朋友們:

快餐:

總結:

三個點:
1.爲何要寫攔截器,而且如何去重寫攔截器,上面說了哦。
2.攔截器文件第一個函數裏是用subject.isPermitted(rolesArray[i])仍是subject.hasRole(rolesArray[i])根據本身在以前設置信息判斷。

我用的perms因此用subject.isPermitted(rolesArray[i])

3.就是這麼向前端返回數據信息

而後貼出四分代碼:
shiroConfig:

package //本身的包;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class shiroConfig {
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/index/login");
        shiroFilterFactoryBean.setSuccessUrl("/Station/noauth");
//        shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
         shiroFilterFactoryBean.setUnauthorizedUrl("/Station/noauth");
//        shiroFilter.setLoginUrl("");//身份認證失敗,則跳轉到登陸頁面的配置 沒有登陸的用戶請求須要登陸的頁面時自動跳轉到登陸頁面,不是必須的屬性,不輸入地址的話會自動尋找項目web項目的根目錄下的」/login.jsp」頁面。
//        shiroFilter.setSuccessUrl("");//登陸成功默認跳轉頁面,不配置則跳轉至」/」。若是登錄前點擊的一個須要登陸的頁面,則在登陸自動跳轉到那個須要登陸的頁面。不跳轉到此。
//        shiroFilter.setUnauthorizedUrl("");//沒有權限默認跳轉的頁面
//        shiroFilter.setFilterChainDefinitions("");//filterChainDefinitions的配置順序爲自上而下,以最上面的爲準
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // <!-- authc:全部url都必須認證經過才能夠訪問; anon:全部url都均可以匿名訪問-->
        filterChainDefinitionMap.put("/Station/**", "anon");
        filterChainDefinitionMap.put("/index/**", "anon");
//        filterChainDefinitionMap.put("/detail/**", "anon");
//        filterChainDefinitionMap.put("/detail/**", "anon");
//        filterChainDefinitionMap.put("/Goods/**", "authc");
        //主要這行代碼必須放在全部權限設置的最後,否則會致使全部 url 都被攔截 剩餘的都須要認證
//        filterChainDefinitionMap.put("/**", "authc");
        filterChainDefinitionMap.put("/detail/getclass","perms[user:teacher]");

        // 一、建立過濾器Map,用來裝自定義過濾器
        LinkedHashMap<String, Filter> map = new LinkedHashMap<>();

        // 二、將自定義過濾器放入map中,若是實現了自定義受權過濾器,那就必須在這裏註冊,不然Shiro不會使用自定義的受權過濾器
        map.put("perms", new MyPermsFilter());

        // 三、將過濾器Ma綁定到shiroFilterFactoryBean上
        shiroFilterFactoryBean.setFilters(map);

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    @Bean
    public SecurityManager securityManager(CustomRealm realm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(realm);
        return  defaultWebSecurityManager;
    }
    @Bean
    public CustomRealm customRealm() {
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return customRealm;
    }
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 使用md5 算法進行加密
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 設置散列次數: 意爲加密幾回
        hashedCredentialsMatcher.setHashIterations(2);

        return hashedCredentialsMatcher;
    }
}

CustomRealm:

package //本身的包;

import com.igeekhome.ccs.biz.IndexBiz;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.apache.tomcat.websocket.AuthenticationException;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.Set;

public class CustomRealm extends AuthorizingRealm {
//    @Autowired
//    private LoginService loginService;
    @Autowired
    IndexBiz indexBiz;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String username = (String) SecurityUtils.getSubject().getPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> stringSet = new HashSet<>();
        if (indexBiz.getsatte(username).equals("老師")){
            System.out.println("老師");
            stringSet.add("user:teacher");
        }else {
            System.out.println("學生");
            stringSet.add("user:student");
        }
        info.setStringPermissions(stringSet);
        return info;
    }

    /**
     * 這裏能夠注入userService,爲了方便演示,我就寫死了賬號了密碼
     * private UserService userService;
     * <p>
     * 獲取即將須要認證的信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
        System.out.println("-------身份認證方法--------");
        String userName = (String) authenticationToken.getPrincipal();
        String userPwd = new String((char[]) authenticationToken.getCredentials());
        //根據用戶名從數據庫獲取密碼
        String password = indexBiz.getpassword(userName);

        if (indexBiz.getsatte(userName)==null) {
            throw new AccountException("用戶名錯誤");
        }
//        else if (!userPwd.equals(password)) {
//            throw new AccountException("密碼不正確");
//        }
        String dbPwd = indexBiz.getpassword(userName);
        return new SimpleAuthenticationInfo(userName,dbPwd, ByteSource.Util.bytes(userName + "salt"),getName());
    }

}

MyPermsFilter:

package //本身的包;

import lombok.SneakyThrows;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.springframework.boot.configurationprocessor.json.JSONObject;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

public class MyPermsFilter extends AuthorizationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
            throws Exception {
        System.out.println("isAccessDenied");
        Subject subject = getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;
        System.out.println("subject:"+subject.getPrincipal());
        if (rolesArray == null || rolesArray.length == 0) {
            //no roles specified, so nothing to check - allow access.
            return true;
        }

        for(int i=0;i<rolesArray.length;i++){
            if(subject.isPermitted(rolesArray[i])){//subject.hasRole(rolesArray[i])
                System.out.println("rolealist:"+rolesArray[i]);
                return true;
            }
        }
        return false;
    }

    @SneakyThrows
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        System.out.println("onAccessDenied");
        Subject subject = getSubject(request, response);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        // If the subject isn't identified, redirect to login URL
        if (subject.getPrincipal() == null) {
//            saveRequestAndRedirectToLogin(request, response);     //沒登錄就進入從新登錄接口,這裏先後端分離不須要
            System.out.println("沒登錄");
            String objectStr= "{'name':'沒登錄'}";
            JSONObject jsonObject=new JSONObject(objectStr);
            PrintWriter wirte = null;
            wirte = response.getWriter();
            wirte.print(jsonObject);
        } else {
            System.out.println("沒權限");
            String objectStr= "{'name':'沒權限'}";
            JSONObject jsonObject=new JSONObject(objectStr);
            PrintWriter wirte = null;
            wirte = response.getWriter();
            wirte.print(jsonObject);
//            JSONObject json = new JSONObject();
//            json.put("state","403");
//            json.put("msg","登陸已失效,請從新登陸!");
//            out.println(json);
//            out.flush();
//            out.close();
        }

        return false;
    }

}

最後

文章的最後給你們安利一個福利,關注公衆號:前程有光,領取一線大廠Java面試題總結+各知識點學習思惟導+一份300頁pdf文檔的Java核心知識點總結!

相關文章
相關標籤/搜索