Spring Security使用JSON格式登录

news2024/11/17 17:25:59

本文内容来自王松老师的《深入浅出Spring Security》,自己在学习的时候为了加深理解顺手抄录的,有时候还会写一些自己的想法。

        Spring Security中默认的登录参数传递的格式是key/value形式,也是表单登录格式。在实际项目中我们可能会通过Json格式来登录来传递参数,这就需要我们自定义登录过滤器来实现。

        其实登录参数的提取是在UsernamePasswordAuthenticationFilter中完成的。如果我们要使用Json格式登录,我们只需要模仿UsernamePasswordAuthenticationFilter过滤器定义自己的过滤器,在将自定义的过滤器放到UsernamePasswordAuthenticationFilter所在的文职即可。

        我们自定义一个LoginFilter:

/**
 * @author tlh
 * @date 2022/11/23 21:27
 */
public class LoginFilter extends UsernamePasswordAuthenticationFilter {

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        //仅支持POST方法
        if (!"POST".equals(request.getMethod())) {
            throw new AuthenticationServiceException("当前认证不支持:" + request.getMethod());
        }
        if (request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_PROBLEM_JSON_UTF8_VALUE)) {
            try {
                Map<String, String> userInfo = new ObjectMapper().readValue(request.getInputStream(), Map.class);
                String userename = userInfo.get(getUsernameParameter());
                String password = userInfo.get(getPasswordParameter());
                UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userename, password);
                setDetails(request, token);
                return this.getAuthenticationManager().authenticate(token);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return super.attemptAuthentication(request, response);
    }
}
  • 首先确保进入该过滤器的请求为POST
  • 根据content-type来判断参数是Json的还是key/value格式的,如果是Json格式的就自己处理,如果不是就调用父类的attemptAuthentication方法来处理即可
  • 如果还是Json格式的数据,则利用jackson提供的ObjectMapper工具将输入流转为Map对象,然后在Map对象里面提取出用户名和密码,接着构造UsernamePasswordAuthenticationToken对象,然后调用AuthenticationManager的authenticate方法来执行认证操作

        其实LoginFilter中,从请求中提取出Json参数之后的逻辑和父类UsernamePasswordAuthenticationFilter中的认证逻辑是一样的,如下是UsernamePasswordAuthenticationFilter获取用户名和密码然后认证的逻辑:

	@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
		if (this.postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}
		String username = obtainUsername(request);
		username = (username != null) ? username.trim() : "";
		String password = obtainPassword(request);
		password = (password != null) ? password : "";
		UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
				password);
		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);
		return this.getAuthenticationManager().authenticate(authRequest);
	}

        LoginFilter定义完之后,接下来我们将其添加到Spring Security过滤器链中去:

/**
 * @author tlh
 * @date 2022/11/21 21:50
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.inMemoryAuthentication().withUser("javagirl")
                .password("{noop}123")
                .roles("admin");
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    LoginFilter loginFilter() throws Exception {
        LoginFilter loginFilter = new LoginFilter();
        loginFilter.setAuthenticationManager(authenticationManagerBean());
        loginFilter.setAuthenticationSuccessHandler((request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(new ObjectMapper().writeValueAsString(authentication));
        });
        return loginFilter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .csrf().disable();
        //表示提起掉原来的UsernamePasswordAuthenticationFilter的位置
        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .antMatchers("/login.html", "/css/**", "/js/**", "/images/**");
    }
}
  •  首先重写configure(AuthenticationManagerBuilder auth)方法来定义一个用户
  • 重写父类的authenticationManagerBean()方法来提供一个AuthenticationManager实例,一会将会配置给LoginFilter
  • 配置LoginFilter实例,同时将AuthenticationManager的实例设置给LoginFilter,然后配置登录成功回调。当然这里也可以设置失败回调
  • 最后在HttpSecurity中,调用addFilterAt方法将LoginFilter过滤器添加到UsernamePasswordAuthenticationFilter过滤器所在的位置

        配置完成后,重启项目,此时我们就可以用Json格式的数据来登录系统了:

        有小伙伴应该会注意到,当我们想要获得一个AuthenticationManager的实例时有两种方法:

  •  重写父类的authenticationManager方法,如下:
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
  • 从写父类的authenticationManagerBean方法,如下:
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

表面上两种方法获取的实例都可以在这里运行,但是实际上是有区别的。第一种方法authenticationManager获取到时全局的AuthenticationManager实例,第二种方法获取到的是局部的AuthenticationManager实例。而LoginFilter作为Spring Security过滤器中的一环,显然该配置局部的AuthenticationManager的实例。应为,如果将全局的AuthenticationManager的实例配置给LoginFilter,则局部的AuthenticationManager实例所对应的用户就会失效。

        实际上,如果我们想要配置一个AuthenticationManager实例,大部分情况下都是通过重写authenticationManagerBean方法来获取。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/30110.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小米蓝牙耳机怎么选?适合小米手机的蓝牙耳机推荐

小米可以说是数码界的一股清流&#xff0c;在手机价格上做出了巨大的贡献&#xff0c;它的产品已经覆盖了我们的生活&#xff0c;包括智能家居、穿戴设备、通讯等等&#xff0c;蓝牙耳机作为出行必备的蓝牙耳机单品&#xff0c;耳机品牌众多&#xff0c;意味着我们有更多的选择…

带你深入了解什么是 Java 线程池技术

我们在程序开发中为了“压榨”计算机的 CPU 资源&#xff0c;会去使用多线程来提高程序的性能&#xff0c;在高并发的场景下&#xff0c;多线程编程显得尤为重要。而在线上&#xff0c;我们使用多线程大部分都是通过线程池来管理。线程池是一种基于池化思想的线程管理工具&…

服务器优化

文章目录服务器负载分析CPU 使用率内存使用率磁盘 I/O平均负载网络使用情况服务器内核参数调优单个进程最大打开文件数TCP 相关设置服务器负载分析 在性能调优时&#xff0c;需要先对服务器负载进行分析&#xff0c;通常而言&#xff0c;我们主要分析 CPU 使用率、内存使用率、…

android——自定义加载按钮LoadingButton

方式一 效果图&#xff1a; simpleButton类代码&#xff1a; package com.oneway.demo.navcontroller.view;import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.content.Context; i…

基于TCP的DNS传输:操作要求

本文档更新了RFC 1123和RFC 1536。本文档要求将允许DNS消息在Internet上通过TCP传输的操作实践作为当前最佳实践。此操作要求与RFC 7766中的实施要求一致。TCP的使用包括基于未加密TCP的DNS以及加密的TLS会话。该文件还考虑了这种形式的DNS通信的后果&#xff0c;以及在不支持当…

腾讯T3整理分享的LeetCode算法小抄完整文档

前言 本文⽬前可以⼿把⼿带你解决 110 道 LeetCode 算法问题&#xff0c;⽽且在不断更新&#xff0c;全部基于 LeetCode 的题⽬&#xff0c;涵盖了所有题型和技巧。 目录 主要内容 ⽬前已包含的 114 道题⽬教程如下&#xff1a; 1.两数之和 10.正则表达式匹配 100.相同的树 …

vue中的transition学习

transition 会在一个元素或组件进入和离开 DOM 时应用动画。他可以将进入和离开的动画应用通过默认插槽传递给它的元素或者组件上 transitionGroup 会在一个 v-for 列表中的元素或组件被插入&#xff0c;移动&#xff0c;或移除时应用动画 <Transition> 组件 进入或者…

[附源码]java毕业设计研究生管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

GoLand 文件增加头部注释

一 背景 为了统一规范,决定在项目中要增加注释,包括文件的头部注释以及函数方法的注释。函数方法的注释好说,文件头部注释这个搞了半天(搞完后才发现真的很容易),记录下自己搞得过程,方便其他人。 二 设置头部注释的步骤 我的系统环境是Macos,这篇文章针对的Mac电…

mitmproxy

我们经常了解到的抓包工具有wireshark、fiddler、charles等&#xff0c;mitmproxy也是一个代理工具&#xff0c;突出的优点是可以命令行方式或脚本的方式进行代理&#xff0c;可以对请求数据进行二次开发&#xff08;二次定制&#xff09; 官网&#xff1a;https://mitmproxy.o…

10 张图解 K8S CNI Calico 网络模型原理与功能实战

一、概述 Calico 是一个联网和网络策略供应商。Calico 支持一套灵活的网络选项&#xff0c;因此你可以根据自己的情况选择最有效的选项&#xff0c;包括非覆盖和覆盖网络&#xff0c;带或不带 BGP。Calico 使用相同的引擎为主机、Pod 和&#xff08;如果使用 Istio 和 Envoy&am…

Nginx:配置

文章目录1、Nginx 工作原理2、Nginx 安装启动2.1、安装2.2、启动3、配置文件3.1、块配置3.2、代理 & 负载均衡3.2.1、代理正向代理反向代理3.2.2、负载均衡3.3、Nginx 缓存3.4、Nginx 限流4、http 配置使用4.1、配置结构4.2、配置命令4.2.1、设置配置命令4.2.2、设置回调方…

你也还在找程序员外包平台吗?有这几个就足够了!

大家都知道&#xff0c;如果程序员想在工作之余赚一点外快的话&#xff0c;接外包是所有兼职赚钱之中来钱比较快的一种。但是要找到一些比较靠谱的&#xff0c;能够经常使用的接外包平台&#xff0c;似乎是一件费时又费力的事情。 接下来就为大家推荐几个比较好的程序员接外包的…

Python_数据容器_列表list

一、数据容器入门 使用场景&#xff1a;批量存储、批量使用多份数据 Python中的数据容器&#xff1a; 一种可以容纳多份数据的数据类型&#xff0c;容纳的每一份数据称之为一个元素。每一个元素&#xff0c;可以是任意类型的数据&#xff0c;如字符串、数字、布尔等。 总结&…

Windows下的RabbitMQ 安装

1.到rabbitmq官网下载安装程序 Messaging that just works — RabbitMQ 1.1 我选择的事RabbitMQ 3.11.3 1.2 点击链接后进入下面的界面 1.3 继续点击RabbitMQ 3.11.3 release 链接 Release RabbitMQ 3.11.3 rabbitmq/rabbitmq-server GitHub 1.4 在页面最下面可以看到下…

葡萄糖-聚乙二醇-四嗪/叶酸/多巴胺 Glucose-PEG-TZ/FA/Dopamine

葡萄糖-聚乙二醇-四嗪/叶酸/多巴胺 Glucose-PEG-TZ/FA/Dopamine 叶酸是一种水溶性维生素&#xff0c;分子式是C19H19N7O6。因绿叶中含量十分丰富而得名&#xff0c;又名蝶酰谷氨酸。在自然界中有几种存在形式&#xff0c;其母体化合物是由蝶啶、对氨基苯甲酸和谷氨酸3种成分结…

购物车案例的实现

最终效果&#xff1a; 1.计算属性 用于计算最终价格&#xff0c;对此计算使用计算属性最佳 原理是遍历books中的每一个属性&#xff0c;价格*数量 computed:{totalPrice(){let totalPrice0// 1.普通的for循环// for (let i0;i<this.books.length;i)// {// totalPriceth…

java+jsp+servlet+mysql高速公路事故管理(交通管理)系统

项目介绍&#xff1a; 一个使用javamysql开发的jspservlet高速公路事故管理系统。 功能主要包括&#xff0c;事故管理&#xff08;可以上传事故照片&#xff09;、车辆信息管理、车主信息管理、违法信息管理、可以按照柱状图&#xff08;饼状图、折线图&#xff09;分析事故…

为什么你需要Twitter群控

爆粉必备 Twitter爆粉的原理是什么&#xff1f; Twitter爆粉怎么实现呢&#xff1f;你先关注别人&#xff0c;然后和人家互动&#xff0c;比如发私信、点赞、评论转发帖子&#xff0c;让别人看到你并回关你&#xff0c;一段时间不回关&#xff0c;你就取消对他们的关注&#…

html中的固定定位的用法

文章目录 前言 一、固定定位的理解&#xff1f; 二、固定定位用在哪里&#xff1f; 1、给大家分享一个案例 2、代码编写 1、先准备一个小案例 2、效果如下&#xff0c;浏览器左上角有个盒子&#xff0c;右边有滚动条 3、我想让他像我们的csdn一样&#xff0c;这个盒子固定在右下…