首页 > 编程语言 >spring security FormLoginConfigure的作用和源码解读

spring security FormLoginConfigure的作用和源码解读

时间:2023-04-11 22:36:49浏览次数:44  
标签:http 登录 spring FormLoginConfigure authFilter 源码 loginProcessingUrl null loginPag

这一节来研究下spring security中FormLoginConfigurer这个配置器的作用

一、综述

FormLoginConfigurer 本质上还是一个SecurityConfigurer,用来对HttpSecurity这个构建器进行配置,它用来对表单登录的功能进行配置,通过HttpSecurity#formLogin()方法来给HttpSecurity 对象中添加此配置器,也就是添加表单登录的功能

二、构造方法

先看下构造方法

public FormLoginConfigurer() {
    // 创建了一个UsernamePasswordAuthenticationFilter,传给了父类构造方法
    super(new UsernamePasswordAuthenticationFilter(), null);
    //设置登录时接收用户名的参数名
    usernameParameter("username");
    //设置登录时接收密码的参数名
    passwordParameter("password");
}
//上边的usernameParameter,passwordParameter可以用来调整登录时的参数名

接下来看下父类AbstractAuthenticationFilterConfigurer的构造方法

private F authFilter;//这个过滤器最后会被添加到DefaultSecurityFilterChain中用来处理表单登录请求

protected AbstractAuthenticationFilterConfigurer(F authenticationFilter,
			String defaultLoginProcessingUrl) {
		//调自己的无参数构造方法
		this();
		//把子类中new出的UsernamePasswordAuthenticationFilter赋值给了类中的属性authFilter
		//这个过滤器最后会被添加到DefaultSecurityFilterChain中用来处理表单登录请求
		this.authFilter = authenticationFilter;
		//这里是在处理接收登录请求的url
		if (defaultLoginProcessingUrl != null) {
			loginProcessingUrl(defaultLoginProcessingUrl);
		}
}

再看下AbstractAuthenticationFilterConfigurer的的无参数构造方法

private String loginPage;
private String loginProcessingUrl;

protected AbstractAuthenticationFilterConfigurer() {
    //设置了登录页面的访问地址为/login
    setLoginPage("/login");
}

private void setLoginPage(String loginPage) {
    //对loginPage这个类属性赋默认值 /login
    this.loginPage = loginPage;
    this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
}

三、loginPage和loginProcessingUrl方法

loginPage方法用来自定义登录页面,loginProcessingUrl用来指定登录请求的url

public FormLoginConfigurer<H> loginPage(String loginPage) {
    //调用父类的方法
    return super.loginPage(loginPage);
}

public T loginProcessingUrl(String loginProcessingUrl) {
		this.loginProcessingUrl = loginProcessingUrl;
	//这样设置以后这个过滤器也就是UsernamePasswordAuthenticationFilter匹配到请求路径
    //是loginProcessingUrl就会把这次请求当做登录来进行处理
    authFilter	.setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));
		return getSelf();
}

父类AbstractAuthenticationFilterConfigurer#loginPage

protected T loginPage(String loginPage) {
    setLoginPage(loginPage);
    //更新认证相关的一些默认值
    updateAuthenticationDefaults();
    this.customLoginPage = true;
    return getSelf();
}

protected final void updateAuthenticationDefaults() {
    if (loginProcessingUrl == null) {
        //如果没有指定loginProcessingUrl就把loginPage作为它
        //所以结合上边的构造方法可以知道默认情况下使用表单登录时loginProcessingUrl
        //和loginPage都是 /login
        loginProcessingUrl(loginPage);
    }
    if (failureHandler == null) {
        //设置登录失败的url
        failureUrl(loginPage + "?error");
    }

    final LogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer(
        LogoutConfigurer.class);
    if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) {
        //退出的Url
        logoutConfigurer.logoutSuccessUrl(loginPage + "?logout");
    }
}

根据上边的源码可以得出一个重要的结论,如果自定义了loginPage就一定也要自定义loginProcessingUrl,

因为如果你只自定义loginPage那么loginProcessingUrl就会等于loginPage,加入你的loginPage="/login.html",显然这个地址是不能处理登录的。

四、init方法

因为FormLoginConfigurer 本质上还是一个SecurityConfigurer,所以它会有一个init方法来进行初始化。

@Override
public void init(H http) throws Exception {
    //调用父类的init方法
    super.init(http);
    //初始化生成登录页面的过滤器
    initDefaultLoginFilter(http);
}

private void initDefaultLoginFilter(H http) {
    //spring security中哪个默认的登录页面就是通过这个过滤器生成的,这里是从共享对象中获取它,
    //它是在DefaultLoginPageConfigurer这个配置器中添加到共享对象中的。
    //共享对象是spring security定义的,用来在配置过程中共享对象的。
    DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
        .getSharedObject(DefaultLoginPageGeneratingFilter.class);
    //底下就是在把通过FormLoginConfigurer设置的一些属性设置到过滤器中,用来生成默认登录页面
    //例如username,password肯定会被作为登录页面中的表单属性。
    if (loginPageGeneratingFilter != null && !isCustomLoginPage()) {
        loginPageGeneratingFilter.setFormLoginEnabled(true);
        loginPageGeneratingFilter.setUsernameParameter(getUsernameParameter());
        loginPageGeneratingFilter.setPasswordParameter(getPasswordParameter());
        loginPageGeneratingFilter.setLoginPageUrl(getLoginPage());
        loginPageGeneratingFilter.setFailureUrl(getFailureUrl());
        loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl());
    }
}

继续看下父类的init方法

@Override
//这个方法就是设置了一些默认值
public void init(B http) throws Exception {
    //设置认证相关的默认值
    updateAuthenticationDefaults();
    updateAccessDefaults(http);
    registerDefaultAuthenticationEntryPoint(http);
}

protected final void updateAuthenticationDefaults() {
    // 如果url为空就设置这个url
    if (loginProcessingUrl == null) {
        //url为空就设置成loginPage也就是上边构造方法传的 /login
        //如果你仅自定义了loginPage,执行到这里就会造成loginPage==loginProcessingUrl
        //所以loginPage,loginProcessingUrl这两个要一起自定义
        loginProcessingUrl(loginPage);
    }
    //登录失败处理器的默认值设置
    if (failureHandler == null) {
        failureUrl(loginPage + "?error");
    }
	//logout配置
    final LogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer(
        LogoutConfigurer.class);
    //如果有logout配置,并且不是用户自定义的,就设置logoutSuccessUrl
    if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) {
        logoutConfigurer.logoutSuccessUrl(loginPage + "?logout");
    }
}

五、configure方法

因为FormLoginConfigurer 本质上还是一个SecurityConfigurer,所以它或者它的父类会有一个configure方法

AbstractAuthenticationFilterConfigurer#configure

//这个方法用来对属性authFilter也就是UsernamePasswordAuthenticationFilter做一些配置
@Override
public void configure(B http) throws Exception {
    PortMapper portMapper = http.getSharedObject(PortMapper.class);
    if (portMapper != null) {
        authenticationEntryPoint.setPortMapper(portMapper);
    }

    RequestCache requestCache = http.getSharedObject(RequestCache.class);
    if (requestCache != null) {
        this.defaultSuccessHandler.setRequestCache(requestCache);
    }
	//这个filter就是构造方法中传过来的UsernamePasswordAuthenticationFilter,
    //在这个filter中会有一个authenticationManager属性,用来验证用户名和密码是否正确
    //这个属性就是在这里赋值的,从共享对象中取出AuthenticationManager
    authFilter.setAuthenticationManager(http
                                        .getSharedObject(AuthenticationManager.class));
    authFilter.setAuthenticationSuccessHandler(successHandler);
    authFilter.setAuthenticationFailureHandler(failureHandler);
    if (authenticationDetailsSource != null) {
        authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
    }
    SessionAuthenticationStrategy sessionAuthenticationStrategy = http
        .getSharedObject(SessionAuthenticationStrategy.class);
    if (sessionAuthenticationStrategy != null) {
        authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
    }
    RememberMeServices rememberMeServices = http
        .getSharedObject(RememberMeServices.class);
    if (rememberMeServices != null) {
        authFilter.setRememberMeServices(rememberMeServices);
    }
    //这是在后处理器对authFilter进行加工,后处理是由使用者提供来对authFilter做进一步处理,
    //大部分情况都没有处理器
    F filter = postProcess(authFilter);
    //把authFilter添加到HttpSecurity中,最终会被加到DefaultSecurityFilterChain中
    http.addFilter(filter);
}

上边提到了从SharedObject中获取AuthenticationManager的对象,那么它是在什么时候放进SharedObject的呢?

是在HttpSecurity的beforeConfigure方法中添加进去的

HttpSecurity

@Override
protected void beforeConfigure() throws Exception {
    //实际是调用了AuthenticationManagerBuilder.build方法来生成对象,
    //这个AuthenticationManagerBuilder也实现了SecurityBuilder,也是一个构件器
    setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build());
}

private AuthenticationManagerBuilder getAuthenticationRegistry() {
    return getSharedObject(AuthenticationManagerBuilder.class);
}

关于AuthenticationManager的作用,可以看这一篇详细了解下 spring seurity表单认证的原理

标签:http,登录,spring,FormLoginConfigure,authFilter,源码,loginProcessingUrl,null,loginPag
From: https://www.cnblogs.com/chengxuxiaoyuan/p/17308098.html

相关文章

  • Collection - PriorityQueue源码解析
    前面以JavaArrayDeque为例讲解了Stack和Queue,其实还有一种特殊的队列叫做PriorityQueue,即优先队列。优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每次取最小元素,C++的优先队列每次取最大元素)。这里牵涉到了大小关系,元素大小的评判可以通过元素本身......
  • springmvc参数传递不给参数值默认值设置方法
    @RequestMapping("hello")publicvoiidtest001(@RequestParam(defaultValue="11")intage,@RequestParam(defaultValue="0.1")doublemoney){System.out.println("age:"+age+",money:"+money);} 注意: ......
  • 一文掌握ArrayList和LinkedList源码解读
    大家好,我是Leo!今天来看一下ArrayList和LinkedList的源码,主要是看一下常用的方法,包括像add、get、remove方法,大部分都是从源码直接解读的,相信大家读完都会有一定收获。ArrayListList<String>list=newArrayList<>();list.add("zly");list.add("coding");list.add("菜......
  • 经典版DD应用系统软件库网站源码支持多方面应用
    demo软件园每日更新资源,请看到最后就能获取你想要的:1.经典版DD应用系统软件库网站源码支持多方面应用DD应用系统软件库网站源码1.增加手机端开发者中心2.增加手机端开发者中心应用管理3.增加手机端开发者中心用户管理4.增加手机端开发者中心网站管理5.增加手机端开发者中......
  • spring 中mongoDB事务配置
    配置事务事务管理器配置代码:@ConfigurationpublicclassTransactionConfig{@BeanMongoTransactionManagertransactionManager(MongoDatabaseFactoryfactory){returnnewMongoTransactionManager(factory);}}在对应方法加上事务注解。事务中只......
  • skywalking 监控 springboot项目
     部署探针打开idea开发工具Run-》EditConfigurations点击Modifyoptions-》AddVMoption-javaagent:E:\projectdeploy\apache-skywalking-apm-9.2.0\apache-skywalking-apm-bin\agent\skywalking-agent.jar-Dskywalking.agent.service_name=service-mylesson-jav......
  • Spring Boot 整合 Kafka
    Kafka环境搭建kafka安装、配置、启动、测试说明:1.安装:直接官网下载安装包,解压到指定位置即可(kafka依赖的Zookeeper在文件中已包含)下载地址:https://kafka.apache.org/downloads示例版本:kafka_2.13-2.8.0.tgz下载后可本地解压安装,解压位置自选,如D:\Java下解压命令:tar......
  • Springboot集成dubbo完整过程(三)
    准备工作1,准备mysql服务环境2,准备redis服务环境3,准备zookeeper服务环境4,准备逆向生成bean的xml配置文件5,准备slf4j日志xml配置文件6,准备一个sql脚本1,搭建创建服务工程1,创建一个空的父工程,用来统一管理依赖2,创建一个interface接口工程,主要存放业务bean,接口类3,创建一......
  • #yyds干货盘点 springboot和vue搭建前后端项目实现员工的增删改查
    前言我是歌谣今天继续带来前后端项目的开发上次已经开发了部门管理,今天继续开发员工管理后端第一步empcontroller代码packagecom.itheima.controller;importcom.itheima.pojo.Emp;importcom.itheima.pojo.PageBean;importcom.itheima.pojo.Result;importcom.itheima.s......
  • SpringBoot整合ElasticSearch8.x 踩坑记录
    背景jdk版本openjdk-17springboot版本2.6.11pom.xml<!--ElasticSearch提供的依赖--><dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>8.6.2</version>......