SpringBoot+Freemarker+Security环境下自建/login登录页面,并解决一直302跳转登录首页、无法登录成功的问题

一. 开发步骤

最近在学习SpringBoot WebSocket编写群聊天的功能,需要用到用户体系,为了方便直接引入了Security包,具体pom如下:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/>
    </parent>
    
    <dependencies>
        <!-- freemarker -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <!-- security安全 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

用FreeMarker开发了一个自己的登录页面login.ftl,放在templates文件夹下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>登录</title>
</head>
<body>
<form action="/login123" method="post">
    <div><label>账号:<input type="text" name="username"/></label></div>
    <div><label>密码:<input type="password" name="password"/></label></div>
    <div><input type="submit" value="登录"/></div>
</form>
</body>
</html>

重写WebSecurityConfig来替换SpringBoot默认的配置,使得我们上面开发的登录页面生效

//@EnableWebSecurity        //SpringBoot自带此注解,不再需要添加
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //设置拦截规则
                .antMatchers("/", "/login")      //对/ 和 /login不拦截
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                //开启默认登录页面
                .formLogin()
                .loginPage("/login")                    //替换Security默认的登录页面到我们自己开发的login.tfl页面
                .loginProcessingUrl("/login123")      //登录提交form action, 也会自动影响Security自带Http Basic登录页的action请求地址
                .defaultSuccessUrl("/chat", true)       //登录成功后,默认后续跳转页面
                .permitAll()
                .and()
                .logout()		//设置注销
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置两个用户
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("user1")
                .password(new BCryptPasswordEncoder().encode("111")).roles("USER").and()
                .withUser("user2")
                .password(new BCryptPasswordEncoder().encode("111")).roles("USER");

        //security5.0以后默认需要加密传输,不能明文
//        auth.inMemoryAuthentication()
//                .withUser("user1").password("111").roles("USER").and()
//                .withUser("user2").password("222").roles("USER");
    }

    @Override
    public void configure(WebSecurity web) {
        //设置不拦截规则,一般用于静态资源
        web.ignoring().antMatchers("/js/**", "pic/");
    }
}

以上代码的重点在.loginPage("/login"),登录界面被替换为view:login。这类似于@Controller方法中返回的String,会被SpringBoot替换为对应的view视图。
/login与页面login.ftl的对应关系,则通过重写WebMvcConfigurer类实现:

//@EnableWebMvc         //SpringBoot下最好不要开启此注解,否则默认SpringMVC配置将失效被替换成本类方法
@Configuration
public class WSWebMvcConfig implements WebMvcConfigurer {

    /**
     * 直接映射url到对应的view:XXX.tfl模板文件
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/ws").setViewName("/freemarker/ws");
        registry.addViewController("/chat").setViewName("/freemarker/chat");
        registry.addViewController("/login").setViewName("/freemarker/login");
    }
}

二. 一直302跳转登录首页问题描述

以上项目启动后,访问/login的URL(我的是:https://localhost:8443/login),可以出现自定义页面
在这里插入图片描述

但输入用户名user1、密码111之后,登录不成功,页面跳转后还是在/login页面内,并且没有明显的报错
通过F12可以看到,http请求被302重定向了,location仍然为登录页
在这里插入图片描述

三. 分析原因

注释掉上面的.loginPage("/login")代码,重新启动服务,/login登录页面会恢复Security默认的登录页面
在这里插入图片描述
尝试登录,可以成功登录并进入后续/chat页面。F12调试模式下,与原先自开发页面请求进行对比,可以发现form请求中多了一个字段:_csrf
在这里插入图片描述

此参数是Security模块为了防止CSRF跨域请求漏洞而添加的伪随机数参数,action调用的/login接口也会对此进行校验,防止漏洞攻击。

至此我们可以定位到之前登录不成功的原因:Spring Security默认是开启crsf验证的,自开发的登录接口被Security模块的CSRF防范机制给拦截了

四. 解决办法

方法一. 去掉Security的CSRF验证机制:
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //设置拦截规则
                .antMatchers("/", "/login")      //对/ 和 /login不拦截
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                //开启默认登录页面
                .formLogin()
                .loginPage("/login")                    //替换Security默认的登录页面到我们自己开发的login.tfl页面
                .loginProcessingUrl("/login123")        //登录提交form action, 也会自动影响Security自带Http Basic登录页的action请求地址
                .defaultSuccessUrl("/chat", true)       //登录成功后,默认后续跳转页面
                .permitAll()
                .and()
                .logout()		//设置注销
                .permitAll()
                .and()
                .csrf().disable();      //Security自带CSRF防攻击,导致页面验证不通过,一直跳转302,这里关闭
    }

注意最后一行,增加.csrf().disable()代码段来关闭CSRF验证。改后重启服务,即可登录成功。

CSRF验证相关原理在:org.springframework.security.web.csrf.CsrfFilter这个过滤器源码中

方法二. 可以修改前端页面,像原生login界面一样增加_csrf字段,上网查到操作如下(thymeleaf):
 <!-- 表单提交用户信息,注意字段的设置,直接是*{} --> 
 <form action="/login" method="post" enctype="application/x-www-form-urlencoded"> 
 	<!--用于验证跨域伪造csrf--> 
 	<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/> 
 	用户名:<input type="text" name="username"/><br/> 
 	密码:<input type="text" name="password"/><br/> 
 	<input type="submit" value="登录"/>
</form> 
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot是一个用于构独立的、基于生产级别的Spring应用程序的框架。而Freemarker是一种模板引擎,用于生成动态内容,特别适合Web应用程序的开发。在Spring Boot中使用Freemarker可以通过引入相应的依赖和配置来实现。 首先,在Spring Boot工程中引入Freemarker依赖。可以通过在pom.xml文件中添加以下依赖来实现: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` 这样,在工程中就可以使用Freemarker来处理模板了。如果想要修改模板文件的位置等配置,可以在application.properties文件中进行配置。比如可以通过以下配置来指定模板文件的位置: ``` spring.freemarker.allow-request-override=false spring.freemarker.allow-session-override=false spring.freemarker.cache=false spring.freemarker.charset=UTF-8 spring.freemarker.check-template-location=true spring.freemarker.content-type=text/html spring.freemarker.expose-request-attributes=false spring.freemarker.expose-session-attributes=false spring.freemarker.suffix=.ftl spring.freemarker.template-loader-path=classpath:/templates/ ``` 这样,Spring Boot就会根据这些配置来加载并解析模板文件,生成相应的动态内容。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Spring Boot 整合 Freemarker](https://blog.csdn.net/yaxuan88521/article/details/117173289)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Springboot整合FreeMarker](https://blog.csdn.net/m0_67402096/article/details/126114796)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭Albert

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值