Spring Security 前后端分离登录,非法请求直接返回 JSON
hello 各位小伙伴,国庆节终于过完啦,松哥也回来啦,今天开始咱们继续发干货!
关于 Spring Security,松哥之前发过多篇文章和大家聊聊这个安全框架的使用:
- 手把手带你入门 Spring Security!
- Spring Security 登录添加验证码
- SpringSecurity 登录使用 JSON 格式数据
- Spring Security 中的角色继承问题
- Spring Security 中使用 JWT!
- Spring Security 结合 OAuth2
不过,今天要和小伙伴们聊一聊 Spring Security 中的另外一个问题,那就是在 Spring Security 中未获认证的请求默认会重定向到登录页,但是在前后端分离的登录中,这个默认行为则显得非常不合适,今天我们主要来看看如何实现未获认证的请求直接返回 JSON ,而不是重定向到登录页面。
前置知识
这里关于 Spring Security 的基本用法我就不再赘述了,如果小伙伴们不了解,可以参考上面的 6 篇文章。
大家知道,在自定义 Spring Security 配置的时候,有这样几个属性:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.formLogin()
.loginProcessingUrl("/doLogin")
.loginPage("/login")
//其他配置
.permitAll()
.and()
.csrf().disable();
}
这里有两个比较重要的属性:
- loginProcessingUrl:这个表示配置处理登录请求的接口地址,例如你是表单登录,那么 form 表单中 action 的值就是这里填的值。
- loginPage:这个表示登录页的地址,例如当你访问一个需要登录后才能访问的资源时,系统就会自动给你通过重定向跳转到这个页面上来。
这种配置在前后端不分的登录中是没有问题的,在前后端分离的登录中,这种配置就有问题了。我举个简单的例子,例如我想访问 /hello 接口,但是这个接口需要登录之后才能访问,我现在没有登录就直接去访问这个接口了,那么系统会给我返回 302,让我去登录页面,在前后端分离中,我的后端一般是没有登录页面的,就是一个提示 JSON,例如下面这样:
@GetMapping("/login")
public RespBean login() {
return RespBean.error("尚未登录,请登录!");
}
完整代码大家可以参考我的微人事项目。
也就是说,当我没有登录直接去访问 /hello 这个接口的时候,我会看到上面这段 JSON 字符串。在前后端分离开发中,这个看起来没问题(后端不再做页面跳转,无论发生什么都是返回 JSON)。但是问题就出在这里,系统默认的跳转是一个重定向,就是说当你访问 /hello 的时候,服务端会给浏览器返回 302,同时响应头中有一个 Location 字段,它的值为 http://localhost:8081/login ,也就是告诉浏览器你去访问 http://localhost:8081/login 地址吧。浏览器收到指令之后,就会直接去访问 http://localhost:8081/login 地址,如果此时是开发环境并且请求还是 Ajax 请求,就会发生跨域。因为前后端分离开发中,前端我们一般在 NodeJS 上启动,然后前端的所有请求通过 NodeJS 做请求转发,现在服务端直接把请求地址告诉浏览器了,浏览器就会直接去访问 http://localhost:8081/login 了,而不会做请求转发了,因此就发生了跨域问题。
解决方案
很明显,上面的问题我们不能用跨域的思路来解决,虽然这种方式看起来也能解决问题,但不是最佳方案。
如果我们的 Spring Security 在用户未获认证的时候去请求一个需要认证后才能请求的数据,此时不给用户重定向,而是直接就返回一个 JSON,告诉用户这个请求需要认证之后才能发起,就不会有上面的事情了。
这里就涉及到 Spring Security 中的一个接口 AuthenticationEntryPoint ,该接口有一个实现类:LoginUrlAuthenticationEntryPoint ,该类中有一个方法 commence,如下:
/**
* Performs the redirect (or forward) to the login form URL.
*/
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) {
String redirectUrl = null;
if (useForward) {
if (forceHttps && "http".equals(request.getScheme())) {
redirectUrl = buildHttpsRedirectUrlForRequest(request);
}
if (redirectUrl == null) {
String loginForm = determineUrlToUseForThisRequest(request, response,
authException);
if (logger.isDebugEnabled()) {
logger.debug("Server side forward to: " + loginForm);
}
RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);
dispatcher.forward(request, response);
return;
}
}
else {
redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);
}
redirectStrategy.sendRedirect(request, response, redirectUrl);
}
首先我们从这个方法的注释中就可以看出,这个方法是用来决定到底是要重定向还是要 forward,通过 Debug 追踪,我们发现默认情况下 useForward 的值为 false,所以请求走进了重定向。
那么我们解决问题的思路很简单,直接重写这个方法,在方法中返回 JSON 即可,不再做重定向操作,具体配置如下:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.formLogin()
.loginProcessingUrl("/doLogin")
.loginPage("/login")
//其他配置
.permitAll()
.and()
.csrf().disable().exceptionHandling()
.authenticationEntryPoint(new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException authException) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
RespBean respBean = RespBean.error("访问失败!");
if (authException instanceof InsufficientAuthenticationException) {
respBean.setMsg("请求失败,请联系管理员!");
}
out.write(new ObjectMapper().writeValueAsString(respBean));
out.flush();
out.close();
}
});
}
在 Spring Security 的配置中加上自定义的 AuthenticationEntryPoint 处理方法,该方法中直接返回相应的 JSON 提示即可。这样,如果用户再去直接访问一个需要认证之后才可以访问的请求,就不会发生重定向操作了,服务端会直接给浏览器一个 JSON 提示,浏览器收到 JSON 之后,该干嘛干嘛。
结语
好了,一个小小的重定向问题和小伙伴们分享下,不知道大家有没有看懂呢?这也是我最近在重构微人事的时候遇到的问题。预计 11 月份,微人事的 Spring Boot 版本会升级到目前最新版,请小伙伴们留意哦。
关注公众号【江南一点雨】,专注于 Spring Boot+微服务以及前后端分离等全栈技术,定期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!

Spring Security 前后端分离登录,非法请求直接返回 JSON的更多相关文章
- 七:Spring Security 前后端分离登录,非法请求直接返回 JSON
Spring Security 前后端分离登录,非法请求直接返回 JSON 解决方案 在 Spring Security 中未获认证的请求默认会重定向到登录页,但是在前后端分离的登录中,这个默认行为则 ...
- vue + spring boot + spring security 前后端分离 携带 Cookie 登录实现 只写了个登录
最近想弄一下vue 所以就自己给自己找坑入 结果弄的满身是伤 哈哈哈 首先我说下 前后端分离 跨域请求 我在网上找了一些 可是都是针对于 spring boot 的 我自己还有 securi ...
- spring boot + spring security +前后端分离【跨域】配置 + ajax的json传输数据
1.前言 网上各个社区的博客参差不齐 ,给初学者很大的困扰 , 我琢磨了一天一夜,到各个社区找资料,然后不断测试,遇到各种坑,一言难尽啊,要么源码只有一部分,要么直接报错... 最后实在不行,直接去看 ...
- 八个开源的 Spring Boot 前后端分离项目,一定要收藏!
八个开源的 Spring Boot 前后端分离项目 最近前后端分离已经在慢慢走进各公司的技术栈,不少公司都已经切换到这个技术栈上面了.即使贵司目前没有切换到这个技术栈上面,我们也非常建议大家学习一下前 ...
- Spring Cloud 前后端分离后引起的跨域访问解决方案
背景 Spring Cloud 微服务试点改造,目前在尝试前后端分离. 前台A应用(本机8080端口),通过网管(本机8769端口)调用后台应用B(本机8082端口).应用C发布的http服务.. A ...
- beego 前后端分离登录验证
conf>app.conf 文件添加一下参数 copyrequestbody=true sessionon =true routers>router.go 文件添加初始化路由 func i ...
- Django前后端分离跨域请求问题
一.问题背景 之前使用django+vue进行前后端分离碰到跨域请求问题,跨域(域名或者端口不同)请求问题的本质是由于浏览器的同源策略导致的,当请求的响应不是处于同一个域名和端口下,浏览器不会接受响应 ...
- 谈谈渲染,玩玩nginx——前后端分离,转发请求到Tomcat的尝试
一.谈谈"渲染" 相信好多人都挺听过"渲染"这个词,但不清楚它是什么意思?前端开发以为这是后端的活儿,后端开发以为是前端的事儿,推着推着就不了了之.其实渲染很简 ...
- 七个开源的 Spring Boot 前后端分离项目,一定要收藏!
前后端分离已经在慢慢走进各公司的技术栈,根据松哥了解到的消息,不少公司都已经切换到这个技术栈上面了.即使贵司目前没有切换到这个技术栈上面,松哥也非常建议大家学习一下前后端分离开发,以免在公司干了两三年 ...
随机推荐
- JavaScript的“true/false && expression”逻辑表达式
true/false && expression 在学习react的过程中,遇到了如下一个方法: function Mailbox(props) { const unreadMessa ...
- Windows平台python验证码识别
参考: http://oatest.dragonbravo.com/Authenticate/SignIn?returnUrl=%2f http://drops.wooyun.org/tips/631 ...
- P2698 [USACO12MAR]花盆Flowerpot 单调队列
https://www.luogu.org/problemnew/show/P2698 警示 用数组写双端队列的话,记得le = 1, ri = 0:le<=ri表示队列非空 题意 求一个最小的 ...
- HDU - 5128The E-pang Palace+暴力枚举,计算几何
第一次写计算几何,ac,感动. 不过感觉自己的代码还可以美化一下. 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5128 题意: 在一个坐标系中,有n个 ...
- HDU4289Control 无向图拆点最大流
/* ** 无向图拆点,求最大流,最大流即为割点个数. */ #include <iostream> #include <cstdio> #include <cstrin ...
- 牛客暑假多校第二场 F trade
题意: 白兔有n个仓库,每个仓库有啊ai个货物,在每个仓库白兔可以装上任意数量的货物,也可以卸下任意数量的货物,现在有k个圆形信号阻隔器,然后有m个顾客下个一个订单,每个顾客的收货量有一个上限, 在每 ...
- Node基础-CommonJS模块化规范
1.在本地项目中基于NPM/YARN安装第三方模块 第一步:在本地项目中创建一个"package.json"的文件 作用:把当前项目所有依赖的第三方模块信息(包含:模块名称以及版本 ...
- Wannafly挑战赛16---A 取石子
链接:https://www.nowcoder.com/acm/contest/113/A来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒空间限制:C/C++ 262144K,其他语言52428 ...
- Storm 系列(六)—— Storm 项目三种打包方式对比分析
一.简介 在将 Storm Topology 提交到服务器集群运行时,需要先将项目进行打包.本文主要对比分析各种打包方式,并将打包过程中需要注意的事项进行说明.主要打包方式有以下三种: 第一种:不加任 ...
- EasyUI总结(一)-- 入门
一.EasyUI下载 EasyUI官方下载地址:http://www.jeasyui.com/download/index.php,目前最新的版本是:jQuery EasyUI 1.4.1