遇到问题:个人项目是先后端分离的,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核心知识点总结!