遇到問題:個人項目是先後端分離的,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; } }
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()); } }
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核心知識點總結!