文章
问答
冒泡
Spring Security自定义表单登录(三)

添加thymeleaf支持

application.yml

spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    encoding: UTF-8

添加依赖

    implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
    implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5"

新增页面login.html

新增页面 /templates/login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自定义登录</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div style="margin: 40px auto; width: 320px;">
        <form action="/login" method="post">
            <div class="mb-3 row">
                <label for="username" class="visually-hidden">用户名</label>
                <input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名">
            </div>
            <div class="mb-3 row">
                <label for="password" class="visually-hidden">密码</label>
                <input type="password" class="form-control" id="password" name="password" placeholder="请输入密码">
            </div>
            <div class="mb-3 row">
                <button type="submit" class="btn btn-primary mb-3">登录</button>
            </div>
        </form>
    </div>
</body>
</html>

控制器跳转 MvcConfig.java

	@Configuration
	public class MvcConfig implements WebMvcConfigurer {
	
	   @Override
	   public void addViewControllers(ViewControllerRegistry registry) {
	       registry.addViewController("/login");
	   }
	
	}

添加配置

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests((requests) -> requests.antMatchers("/public").permitAll()
                .antMatchers(HttpMethod.GET, "/login").permitAll()
                .antMatchers("/admin").hasAnyRole("ADMIN")).cors().disable();

        http.formLogin()
                // 登录页面的路径
                .loginPage("/login");
        http.httpBasic();
    }

注意:

  • csrf().disable(); csrf会拦截POST请求,需要禁用

  • .loginPage("/login"); 设置登录页面为/login

  • antMatchers(HttpMethod.GET, "/login").permitAll(); 登录页面不需要认证

配置认证成功失败处理器

    final AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
    final AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler("/login?error");

    http.formLogin()
            .successHandler((request, response, authentication) -> {
                log.info("{} 登录成功", authentication.getName());
                // 跳转到认证前访问到地址(默认就是这个处理器)
                successHandler.onAuthenticationSuccess(request, response, authentication);
            })
            .failureHandler((request, response, exception) -> {
                log.error("登录失败 {}", exception.getMessage());
                // 会将exception放入session中,页面可以通过session获取异常
                failureHandler.onAuthenticationFailure(request, response, exception);
//                    response.sendRedirect("/login?error=" + exception.getMessage());
            })
            });

login.html 显示错误信息

    <th:block th:if="${session.SPRING_SECURITY_LAST_EXCEPTION != null}">
        <div th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}"></div>
    </th:block>

配置退出登录

    http.formLogin()
            .logout()
            .logoutSuccessHandler((request, response, authentication) -> {
                log.info("{} 退出登录", authentication.getName());
                response.sendRedirect("/login");
            });
GET http://localhost:8080/logout

配置异常处理器

	final LoginUrlAuthenticationEntryPoint loginUrlAuthenticationEntryPoint = new LoginUrlAuthenticationEntryPoint("/login");

    http.authorizeRequests((requests) -> requests.antMatchers("/public").permitAll()
            .antMatchers(HttpMethod.GET, "/login").permitAll()
            .antMatchers("/admin").hasAnyRole("ADMIN")
            .anyRequest().authenticated())
            .exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
            log.warn("认证异常");
            // 默认就是LoginUrlAuthenticationEntryPoint
            loginUrlAuthenticationEntryPoint.commence(request, response, authException);
    })
            .and()
            .exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> {
        // 403 Forbidden 没有授权,执行到此处
        log.warn("没有权限");
        HttpServletResponseUtils.write(response, "text/plain", "403 Forbidden");
    });
  • authenticationEntryPoint:它在用户请求处理过程中遇到认证异常时

  • accessDeniedHandler: 没有访问权限

ExceptionTranslationFilter.java

	private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,
			FilterChain chain, RuntimeException exception) throws IOException, ServletException {
		if (exception instanceof AuthenticationException) {
			handleAuthenticationException(request, response, chain, (AuthenticationException) exception);
		}
		else if (exception instanceof AccessDeniedException) {
			handleAccessDeniedException(request, response, chain, (AccessDeniedException) exception);
		}
	}

关于作者

Rick
获得点赞
文章被阅读