前端界面接收json数据(前后端分离中使用)
前端界面接收json数据(前后端分离中使用)
2024-11-06 01:40:22  作者:依然那么爱  网址:https://m.xinb2b.cn/know/cca467158.html

做微人事的小伙伴(https://github.com/lenve/vhr),应该都发现了在微人事中有一个极为特殊的请求,那就是登录。

登录请求是一个 POST 请求,但是数据传输格式是 key/value 的形式。整个项目里就只有这一个 POST 请求是这样,其他 POST 请求都是 json 格式的数据。

为什么做成这个样子呢?还是懒呗。

因为 Spring Security 中默认的登录数据格式就是 key/value 的形式,一直以来懒得改。最近刚好在录 Spring Security,就抽空把这里调整了下,这样前后端就能统一起来了。

好了,我们一起来看下怎么实现。

1.服务端接口调整

首先大家知道,用户登录的用户名/密码是在 UsernamePasswordAuthenticationFilter 类中处理的,具体的处理代码如下:

public Authentication attemptAuthentication(HttpServletrequest request,HttpServletResponse response) throws AuthenticationException {String username = obtainUsername(request);String password = obtainPassword(request); //省略}protected String obtainPassword(HttpServletRequest request) {return request.getParameter(passwordParameter);}protected String obtainUsername(HttpServletRequest request) {return request.getParameter(usernameParameter);}

从这段代码中,我们就可以看出来为什么 Spring Security 默认是通过 key/value 的形式来传递登录参数,因为它处理的方式就是 request.getParameter。

所以我们要定义成 JSON 的,思路很简单,就是自定义来定义一个过滤器代替 UsernamePasswordAuthenticationFilter ,然后在获取参数的时候,换一种方式就行了。

「这里有一个额外的点需要注意,就是我们的微人事现在还有验证码的功能,所以如果自定义过滤器,要连同验证码一起处理掉。」

2.自定义过滤器

接下来我们来自定义一个过滤器代替 UsernamePasswordAuthenticationFilter ,如下:

public class LoginFilter extends UsernamePasswordAuthenticationFilter { @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (!request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " request.getMethod()); } String verify_code = (String) request.getSession().getAttribute("verify_code"); if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)) { Map<String, String> loginData = new HashMap<>(); try { loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class); } catch (IOException e) { }finally { String code = loginData.get("code"); checkCode(response, code, verify_code); } String username = loginData.get(getUsernameParameter()); String password = loginData.get(getPasswordParameter()); if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } else { checkCode(response, request.getParameter("code"), verify_code); return super.attemptAuthentication(request, response); } } public void checkCode(HttpServletResponse resp, String code, String verify_code) { if (code == null || verify_code == null || "".equals(code) || !verify_code.toLowerCase().equals(code.toLowerCase())) { //验证码不正确 throw new AuthenticationServiceException("验证码不正确"); } }}

这段逻辑我们基本上是模仿官方提供的 UsernamePasswordAuthenticationFilter 来写的,我来给大家稍微解释下:

首先登录请求肯定是 POST,如果不是 POST ,直接抛出异常,后面的也不处理了。因为要在这里处理验证码,所以第二步从 session 中把已经下发过的验证码的值拿出来。接下来通过 contentType 来判断当前请求是否通过 JSON 来传递参数,如果是通过 JSON 传递参数,则按照 JSON 的方式解析,如果不是,则调用 super.attemptAuthentication 方法,进入父类的处理逻辑中,也就是说,我们自定义的这个类,既支持 JSON 形式传递参数,也支持 key/value 形式传递参数。如果是 JSON 形式的数据,我们就通过读取 request 中的 I/O 流,将 JSON 映射到一个 Map 上。从 Map 中取出 code,先去判断验证码是否正确,如果验证码有错,则直接抛出异常。验证码的判断逻辑,大家可以参考:松哥手把手教你给微人事添加登录验证码。接下来从 Map 中取出 username 和 password,构造 UsernamePasswordAuthenticationToken 对象并作校验。

过滤器定义完成后,接下来用我们自定义的过滤器代替默认的 UsernamePasswordAuthenticationFilter,首先我们需要提供一个 LoginFilter 的实例:

@BeanLoginFilter loginFilter() throws Exception { LoginFilter loginFilter = new LoginFilter(); loginFilter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { response.setContentType("application/json;charset=utf-8"); PrintWriter out = response.getWriter(); Hr hr = (Hr) authentication.getPrincipal(); hr.setPassword(null); RespBean ok = RespBean.ok("登录成功!", hr); String s = new ObjectMapper().writeValueAsString(ok); out.write(s); out.flush(); out.close(); } }); loginFilter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { response.setContentType("application/json;charset=utf-8"); PrintWriter out = response.getWriter(); RespBean respBean = RespBean.error(exception.getMessage()); if (exception instanceof LockedException) { respBean.setMsg("账户被锁定,请联系管理员!"); } else if (exception instanceof CredentialsExpiredException) { respBean.setMsg("密码过期,请联系管理员!"); } else if (exception instanceof AccountExpiredException) { respBean.setMsg("账户过期,请联系管理员!"); } else if (exception instanceof DisabledException) { respBean.setMsg("账户被禁用,请联系管理员!"); } else if (exception instanceof BadCredentialsException) { respBean.setMsg("用户名或者密码输入错误,请重新输入!"); } out.write(new ObjectMapper().writeValueAsString(respBean)); out.flush(); out.close(); } }); loginFilter.setAuthenticationManager(authenticationManagerBean()); loginFilter.setFilterProcessesUrl("/doLogin"); return loginFilter;}

当我们代替了 UsernamePasswordAuthenticationFilter 之后,原本在 SecurityConfig#configure 方法中关于 form 表单的配置就会失效,那些失效的属性,都可以在配置 LoginFilter 实例的时候配置。

另外记得配置一个 AuthenticationManager,根据 WebSecurityConfigurerAdapter 中提供的配置即可。

FilterProcessUrl 则可以根据实际情况配置,如果不配置,默认的就是 /login。

最后,我们用自定义的 LoginFilter 实例代替 UsernamePasswordAuthenticationFilter,如下:

@Overrideprotected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() ... //省略 http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);}

调用 addFilterAt 方法完成替换操作。

篇幅原因,我这里只展示了部分代码,完整代码小伙伴们可以在 GitHub 上看到:https://github.com/lenve/vhr。

配置完成后,重启后端,先用 POSTMAN 测试登录接口,如下:


3.前端修改

原本我们的前端登录代码是这样的:

this.$refs.loginForm.validate((valid) => { if (valid) { this.loading = true; this.postKeyValueRequest('/doLogin', this.loginForm).then(resp => { this.loading = false; //省略 }) } else { return false; }});

首先我们去校验数据,在校验成功之后,通过 postKeyValueRequest 方法来发送登录请求,这个方法是我自己封装的通过 key/value 形式传递参数的 POST 请求,如下:

export const postKeyValueRequest = (url, params) => { return axios({ method: 'post', url: `${base}${url}`, data: params, transformRequest: [function (data) { let ret = ''; for (let i in data) { ret = encodeURIComponent(i) '=' encodeURIComponent(data[i]) '&' } return ret; }], headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });}export const postRequest = (url, params) => { return axios({ method: 'post', url: `${base}${url}`, data: params })}

postKeyValueRequest 是我封装的通过 key/value 形式传递参数,postRequest 则是通过 JSON 形式传递参数。

所以,前端我们只需要对登录请求稍作调整,如下:

this.$refs.loginForm.validate((valid) => { if (valid) { this.loading = true; this.postRequest('/doLogin', this.loginForm).then(resp => { this.loading = false; //省略 }) } else { return false; }});

配置完成后,再去登录,浏览器按 F12 ,就可以看到登录请求的参数形式了:


好啦,这就是松哥和大家介绍的 SpringSecurity JSON 验证码登录,「如果觉得还不错,记得点一下右下角在看哦。」

完整代码小伙伴们可以在 GitHub 上下载:https://github.com/lenve/vhr

  • 唐宋八大家是哪八位(中国的唐宋八大家是哪八位)
  • 2024-11-06中国的唐宋八大家是哪八位唐宋八大家,又称唐宋散文八大家,是唐朝韩愈、柳宗元和北宋欧阳修、王安石、苏轼、苏洵、苏辙、曾巩八位著名散文家的合称关于这八个人,很多人的常规印象都是文采飞扬的文学家,或者是闲情逸致,抚琴饮酒的艺术家但。
  • 长期坚持跑步的13个益处(每天坚持40分钟跑步)
  • 2024-11-06每天坚持40分钟跑步你喜欢跑步吗?记得小时候比较喜欢跑步,但是大了之后很少会主动体验奔跑的感觉,现在是越来越懒,早已经习惯了,久坐不运动,足不出户享受各种美食这样的生活习惯也给身体带来了诸多健康问题,健身似乎是一种改善肥。
  • 江姐拌演者于蓝(于蓝永远的江姐)
  • 2024-11-06于蓝永远的江姐中央纪委国家监委网站李云舒6月27日晚,著名电影表演艺术家于蓝在北京去世,享年99岁于蓝是新中国“22大电影明星”之一,曾主演《白衣战士》《翠岗红旗》《革命家庭》《烈火中永生》等电影,在新中国银幕上塑。
  • 骂架很厉害的女孩子(说脏话骂女人后台开战)
  • 2024-11-06说脏话骂女人后台开战“偶像”在小编的定义里是一个有正确价值观,能够带给粉丝或者观众一定想法或者在自己专业领域内十分出众的演员或者歌手才有资格被称之为偶像,但是最近一段时间,小编对于偶像的看法接连遭到无情的打击,只因为网络。
  • 如何把豆沙包做得漂亮(教你这简单方法做豆沙包)
  • 2024-11-06教你这简单方法做豆沙包嗨,大家好,欢迎来到小圆的厨房,今天分享的是“不会捏褶子?教你这简单方法做豆沙包,再不担心褶子捏不好啦!”这几天的天气不大好,不是大雨就是中雨的,而且气温一下子降到了六度,前几天最高气温可是有22呀!。
  • 活性炭是什么东西(活性炭介绍)
  • 2024-11-06活性炭介绍活性炭是一种经特殊处理的炭,将有机原料(果壳、煤、木材等)在隔绝空气的条件下加热,以减少非碳成分(此过程称为炭化),然后与气体反应,表面被侵蚀,产生微孔发达的结构(此过程称为活化)由于活化的过程是一个。
  • 梅州五华旅游景点大全6日游(收藏春节去哪玩)
  • 2024-11-06收藏春节去哪玩农历兔年春节即将来临,小伙伴们想好去哪里玩了吗?记者从五华县文化广电旅游体育局获悉,今年春节假期,该县各景区为大家准备了丰富多彩的活动,一起来看看吧新丰寨旅游景区景区内以新丰茶、樟树、沉香、梅片等多类。
  • 有效服务乡村振兴(坚持为农服务助力乡村振兴)
  • 2024-11-06坚持为农服务助力乡村振兴9月,走进浙江桐乡市乌镇镇董家村,以“白、嫩、壮”独特品质享誉长三角地区的农产品——董家茭白随风摇曳,沙沙作响“乡村要振兴,离不开产业支撑在综合改革中,我们大力推进合作经营,培育良种,规范种植,让20。
  • 炸猪油的熬制方法(不要直接下锅炸)
  • 2024-11-06不要直接下锅炸导语:熬猪油,不要直接下锅炸!教您正确做法,猪油洁白如玉,香味十足夏天来了,弄得人食欲下降,吃饭不香,吃肉觉得太油腻,不吃肉又觉得太素,想好好的吃顿饭太难了,不知道你家有没有这种情况?如果有,我建议这。
  • 何洁与赫子铭离婚背后的真相(对比赫子铭离婚后手撕何洁)
  • 2024-11-06对比赫子铭离婚后手撕何洁何洁和赫子铭自宣布离婚后便风波不断,好不容易等到了法院开庭,面临孩子抚养和财产分割问题的时候,事情的走向似乎越来越扑朔迷离了离婚案开庭没几天,何洁被拍到与刁磊现身医院,行为举止颇为亲密,网上还爆出两人。
  • 靓丽风景线作文五年级(一道亮丽的风景)
  • 2024-11-06一道亮丽的风景今天,在回家的路上,看到前面围了很多人,往里一瞧,路面上躺着一位老奶奶,不用说,这又是一起车祸虽然围观的人很多,但没有一个人上前扶一把,拨一下“120”,或者“110”的这时一位叔叔的脚向前迈了一步,。
  • 上海不戴头盔抓拍吗(上海严查不戴头盔)
  • 2024-11-06上海严查不戴头盔今年5月1日《上海市非机动车安全管理条例》实施至今已有两个月时间这两个月以来上海非机动车骑乘人员守法意识明显提高据悉上海戴盔率达到90%以上2021年5月1日至7月7日涉及电动自行车死亡交通事故数同比。