添加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);
}
}
Github:https://github.com/jkxyx205/spring-security-learn/tree/form-configurer