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,那就是你本来就没有权限!

好了好了说了一大堆。

老规矩,给那些赶时间的朋友们:

快餐:

总结:

三个点:

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核心知识点总结!

秒极啊!手把手带你进行shiro授权拦截器的重写,学到了学到了的更多相关文章

  1. shiro中拦截器机制

    8.1 拦截器介绍 Shiro使用了与Servlet一样的Filter接口进行扩展:所以如果对Filter不熟悉可以参考<Servlet3.1规范>http://www.iteye.com ...

  2. Shiro授权流程

    1,授权中涉及的一些概念      [1]授权:访问控制,即在应用中认证用户能否访问的系统资源(如一个页面,一个按钮等).      [2]资源:在Web应用中反应为用户可以访问的URL.       ...

  3. 第八章 拦截器机制——《跟我学Shiro》

    转发地址:https://www.iteye.com/blog/jinnianshilongnian-2025656 博客分类: 跟我学Shiro 跟我学Shiro  目录贴:跟我学Shiro目录贴 ...

  4. Shiro学习(8)拦截器机制

    8.1 拦截器介绍 Shiro使用了与Servlet一样的Filter接口进行扩展:所以如果对Filter不熟悉可以参考<Servlet3.1规范>http://www.iteye.com ...

  5. Shiro Web集成及拦截器机制(四)

    Shiro与 Web 集成 Shiro 提供了与 Web 集成的支持,其通过一个 ShiroFilter 入口来拦截需要安全控制的 URL,然后进行相应的控制,ShiroFilter 类似于如 Str ...

  6. [转帖]从零开始入门 K8s | 手把手带你理解 etcd

    从零开始入门 K8s | 手把手带你理解 etcd https://zhuanlan.zhihu.com/p/96721097 导读:etcd 是用于共享配置和服务发现的分布式.一致性的 KV 存储系 ...

  7. 手把手带你体验鸿蒙 harmonyOS

    wNlRGd.png 前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 image.png 一.为什么要尝鲜 harmonyos? wNlfx ...

  8. 手把手带你使用EFR32 -- 土壤湿度传感器变身第二形态,以 ZigBee 形态出击

    前言 后悔,总之就是非常后悔,我当时到底是为啥才会猪油蒙心,选择了 EFR32 来学习 ZigBee 使用啊? EFR32 这玩意看性能确实不错,但是资料太少了,EmberZnet SDK 也是用得一 ...

  9. [.Net] 手把手带你将自己打造的类库丢到 NuGet 上

    手把手带你将自己打造的类库丢到 NuGet 上 序 我们习惯了对项目右键点击“引用”,选择“管理NuGet 程序包”来下载第三方的类库,可曾想过有一天将自己的打造的类库放到 NuGet 上,让第三者下 ...

随机推荐

  1. 微信小程序-TodoList

    TodoList 博客班级 https://edu.cnblogs.com/campus/zjcsxy/SE2020 作业要求 https://edu.cnblogs.com/campus/zjcsx ...

  2. 一文搞懂后台高性能服务器设计的常见套路, BAT 高频面试系列

    微信搜索「编程指北」,关注这个写干货的程序员,回复「资源」,即可获取后台开发学习路线和书籍 先赞后看,养成习惯~ 前言 金九银十,又是一年校招季. 经历过,才深知不易.最近,和作为校招面试官的同事聊了 ...

  3. 5 Post和Get

    5 Post和Get GET和POST有什么区别?及为什么网上的多数答案都是错的 知乎回答 get: RFC 2616 - Hypertext Transfer Protocol -- HTTP/1. ...

  4. Java多线程经典题目(医院挂号)

    题目 实现一个医院的挂号机系统,要求:有多台挂号机同时运行,此时无论有多少患者挂号,要求都能挂到不同 的号码,并且要求实现当意外断电之后,下一次恢复还能从上次结束号码继续挂号? * synchroni ...

  5. 内核搞笑的bug不少

  6. nginx&http 第四章 ngx http代理 && 转载

    Nginx访问上游服务器的流程大致分以下几个阶段:启动upstream.连接上游服务器.向上游发送请求.接收上游响应(包头/包体).结束请求. upstream相关的两个重要数据结构ngx_http_ ...

  7. 删除ceph集群mds

    ceph集群新搭建以后是只有一个默认的存储池rbd的池 ## 创建文件接口集群 1.创建一个元数据池 [root@mytest ~]# ceph osd pool create metadata 20 ...

  8. 基础网络路由命令(tracert、route print 、netstat )

    网络知识有限,平时自己积累,捣鼓自己电脑使用,如是一样菜鸟,请勿自行在服务器端使用. 快捷键Ctrl+C  结束跟踪 快捷键    ↑      可以查询上次输入的命令 window+R组合键,输入C ...

  9. Android10_原理机制系列_AMS(ATMS)之应用的第一次启动的过程

    概述 该篇基于Android 10的代码.在 AMS之AMS的启动---Android Framework(Android 10) 中已经介绍了,在Android 10中,activity的调度和管理 ...

  10. 玩转百度地图API(地图,坐标,标记,添加控件,2D图,混合图,智能搜索,地址解析器,信息窗口)

    1.注册得到appkey 2.直接上代码 <!DOCTYPE html> <html> <head> <meta http-equiv="Conte ...