shiro 整合前后端拆散的 springboots,Vue 我的项目真的是有很多大坑啊。
明天我的主题是:如何设置 shiro 过滤器。
遇到问题:我的我的项目是前后端拆散的,shiro 外面有一个 shiroFilterFactoryBean.setUnauthorizedUrl(“你本人的 url”);
函数
这是什么意思呢:这示意如果你拜访了一个须要权限的 url,然而目前你登陆的角色没有权限,那么页面默认跳转的地址。
看着仿佛是没啥故障。
然而!!!
如果是前后端拆散怎么办呢?后端 shiro 让我的项目跳向前端某个页面,这里是前后端分离式的啊!以后端的用户(没权限)发送了一个申请被 shiro 间接拦挡了,前端一脸懵不晓得产生什么问题了,所以傻傻的在控制台返回一段红色的跨域错误信息。
那么咱们应该怎么解决呢?
你必定会想那就让后端让前端跳转啊,这里我要说,既然是前后端拆散的话,后端只能给前端发送信息让前端依据返回的信息自行处理。
然而问题又来了,shiro 这玩意间接把前端申请拦挡了啥都不返回,啥都不说一声,搞的前端还误会是跨域问题。
那么咱们就应该去改写 shiro 的拦截器,让它解决形式更人性化。
不废话了,开始主题
开始改写拦截器:
//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;
}
}
首先看到
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
再看到代码:
这是看看你之前有没有存权限,如果没存的话(列表大小为 0 或列表为空)返回 true,shiro 就不拦你了。
再往下看:
这里 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,那就是你原本就没有权限!
好了好了说了一大堆。
[](https://blog.csdn.net/lanlans…
老规矩,给那些赶时间的敌人们:
快餐:
总结:
三个点:
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]");
// 1、创立过滤器 Map,用来装自定义过滤器
LinkedHashMap<String, Filter> map = new LinkedHashMap<>();
// 2、将自定义过滤器放入 map 中,如果实现了自定义受权过滤器,那就必须在这里注册,否则 Shiro 不会应用自定义的受权过滤器
map.put("perms", new MyPermsFilter());
// 3、将过滤器 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 外围知识点总结!