Skip to content

Commit fa0f5d8

Browse files
author
“whgojp”
committed
add ValidateCodeFilter and perfect CustomSimpleUrlAuthenticationFailureHandler
1 parent 829dfac commit fa0f5d8

File tree

7 files changed

+54
-55
lines changed

7 files changed

+54
-55
lines changed

src/main/java/top/whgojp/common/enums/LoginError.java

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package top.whgojp.common.enums;
22

3-
import lombok.Data;
43

54
import java.util.HashMap;
65
import java.util.Map;
@@ -16,17 +15,17 @@ public enum LoginError {
1615

1716
FAILURE(0, "登录失败!"),
1817

19-
BADCREDENTIALS(1, "用户名密码错误!"),
18+
BADCREDENTIALS(1, "用户名或密码错误!"),
2019

2120
LOCKED(2, "用户已被锁定,无法登录!"),
2221

2322
ACCOUNTEXPIRED(3, "用户已过时,无法登录!"),
2423

25-
USERNAMENOTFOUND(4, "用户不存在!");
24+
USERNAMENOTFOUND(4, "用户不存在!"),
2625

27-
// CAPTCHANOTFOUND(5,'验证码不能为空!');
28-
// CAPTCHAEXPIRED(6,'验证码已过期!');
29-
// CAPTCHAERROR(7,'验证码错误!');
26+
CAPTCHANOTFOUND(5,"验证码不能为空!"),
27+
CAPTCHAEXPIRED(6,"验证码已过期!"),
28+
CAPTCHAERROR(7,"验证码错误!");
3029

3130

3231
private Integer type;
@@ -38,12 +37,12 @@ public enum LoginError {
3837
static {
3938
mappings.put(FAILURE.type, FAILURE);
4039
mappings.put(BADCREDENTIALS.type, BADCREDENTIALS);
41-
mappings.put(LOCKED.type, BADCREDENTIALS);
42-
mappings.put(ACCOUNTEXPIRED.type, BADCREDENTIALS);
43-
mappings.put(USERNAMENOTFOUND.type, BADCREDENTIALS);
44-
// mappings.put(CAPTCHANOTFOUND.type, BADCREDENTIALS);
45-
// mappings.put(CAPTCHAEXPIRED.type, BADCREDENTIALS);
46-
// mappings.put(CAPTCHAERROR.type, BADCREDENTIALS);
40+
mappings.put(LOCKED.type, LOCKED);
41+
mappings.put(ACCOUNTEXPIRED.type, ACCOUNTEXPIRED);
42+
mappings.put(USERNAMENOTFOUND.type, USERNAMENOTFOUND);
43+
mappings.put(CAPTCHANOTFOUND.type, CAPTCHANOTFOUND);
44+
mappings.put(CAPTCHAEXPIRED.type, CAPTCHAEXPIRED);
45+
mappings.put(CAPTCHAERROR.type, CAPTCHAERROR);
4746

4847
}
4948

src/main/java/top/whgojp/common/filter/ValidateCodeFilter.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import cn.hutool.core.util.StrUtil;
44
import lombok.SneakyThrows;
55
import lombok.extern.slf4j.Slf4j;
6-
import org.springframework.security.core.AuthenticationException;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
import org.springframework.stereotype.Component;
78
import org.springframework.util.AntPathMatcher;
89
import org.springframework.web.filter.OncePerRequestFilter;
910
import top.whgojp.common.constant.SysConstant;
@@ -24,22 +25,22 @@
2425
* @Date: 2024/6/21 19:45
2526
*/
2627
@Slf4j
28+
@Component
2729
public class ValidateCodeFilter extends OncePerRequestFilter {
2830
private AntPathMatcher pathMatcher = new AntPathMatcher();
31+
@Autowired
2932
private CustomSimpleUrlAuthenticationFailureHandler customSimpleUrlAuthenticationFailureHandler;
3033

3134
@SneakyThrows
3235
@Override
3336
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
3437
String url = request.getRequestURI();
35-
if (pathMatcher.match(SysConstant.LOGIN_URL, url)) {
36-
final String captchaCheck = "11111";
38+
if (pathMatcher.match(SysConstant.LOGIN_PROCESS, url) && request.getMethod().equalsIgnoreCase("post")) {
3739
String captcha = request.getParameter("captcha");
38-
if (captcha == null) captcha = captchaCheck;
39-
if (captcha == captchaCheck) return;
4040

4141
if (StrUtil.isBlank(captcha)) {
4242
CustomAuthenticationException exception = new CustomAuthenticationException("验证码为空");
43+
// log.error(exception.getMessage());
4344
customSimpleUrlAuthenticationFailureHandler.onAuthenticationFailure(request, response, exception);
4445
return;
4546
}
@@ -49,17 +50,19 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
4950

5051
if (StrUtil.isEmpty(captchaCode)) {
5152
CustomAuthenticationException exception = new CustomAuthenticationException("验证码过期");
53+
// log.error(exception.getMessage());
5254
customSimpleUrlAuthenticationFailureHandler.onAuthenticationFailure(request, response, exception);
5355
return;
5456
}
5557

5658
if (!captcha.equalsIgnoreCase(captchaCode)) {
5759
CustomAuthenticationException exception = new CustomAuthenticationException("验证码不正确");
60+
// log.error("验证码不正确" + ";用户输入验证码:" + captcha + ";正确验证码:" + captchaCode);
5861
customSimpleUrlAuthenticationFailureHandler.onAuthenticationFailure(request, response, exception);
5962
return;
6063
}
61-
log.info("验证码正确,用户输入:" + captcha, "session存储:" + captchaCode);
62-
filterChain.doFilter(request, response);
64+
log.info("验证码正确,用户输入:" + captcha + "; session存储:" + captchaCode);
6365
}
66+
filterChain.doFilter(request, response);
6467
}
6568
}

src/main/java/top/whgojp/modules/system/controller/LoginController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import javax.servlet.http.HttpServletResponse;
2525
import javax.servlet.http.HttpSession;
2626
import java.io.IOException;
27-
import java.util.UUID;
2827

2928
/**
3029
* @description 登录处理
@@ -91,6 +90,7 @@ public String hello() {
9190
}
9291

9392

93+
9494
@PostMapping("/authenticate")
9595
public ResponseEntity<AuthenticationResponse> createAuthenticateToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception {
9696
Authentication authenticate = null;

src/main/java/top/whgojp/security/SecurityConfigurer.java

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,40 @@
22

33
import org.springframework.beans.factory.annotation.Autowired;
44
import org.springframework.context.annotation.Bean;
5-
import org.springframework.security.authentication.AccountExpiredException;
5+
import org.springframework.context.annotation.Configuration;
66
import org.springframework.security.authentication.AuthenticationManager;
7-
import org.springframework.security.authentication.BadCredentialsException;
8-
import org.springframework.security.authentication.LockedException;
97
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
108
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
119
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
1210
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
13-
import org.springframework.security.core.userdetails.UsernameNotFoundException;
1411
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
1512
import org.springframework.security.crypto.password.PasswordEncoder;
1613
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
1714
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
18-
import org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler;
1915
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
2016
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
2117
import org.springframework.web.cors.CorsConfiguration;
2218
import org.springframework.web.cors.CorsConfigurationSource;
2319
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
2420
import top.whgojp.common.config.AuthIgnoreConfig;
2521
import top.whgojp.common.constant.SysConstant;
22+
import top.whgojp.common.filter.ValidateCodeFilter;
2623
import top.whgojp.common.push.service.EmailPush;
2724
import top.whgojp.security.detail.CustomUserDetailsService;
2825
import top.whgojp.security.handler.CustomLogoutSuccessHandler;
2926
import top.whgojp.security.handler.CustomSavedRequestAwareAuthenticationSuccessHandler;
3027
import top.whgojp.security.handler.CustomSimpleUrlAuthenticationFailureHandler;
31-
import top.whgojp.common.enums.LoginError;
3228

33-
import java.util.HashMap;
3429
import java.util.List;
35-
import java.util.Map;
3630

31+
@Configuration
3732
@EnableWebSecurity
3833
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
3934

4035
@Autowired
4136
private AuthIgnoreConfig authIgnoreConfig;
37+
@Autowired
38+
private ValidateCodeFilter validateCodeFilter;
4239

4340
@Autowired
4441
private CustomUserDetailsService customUserDetailsService;
@@ -57,19 +54,21 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
5754
auth
5855
// 不做具体的 AuthenticationManager 选择这里的默认使用 DaoAuthenticationConfigurer
5956
// 这个 DetailsService 单纯就是从 Dao 层取得用户数据,它不进行密码校验
60-
.userDetailsService(customUserDetailsService)
57+
.userDetailsService(customUserDetailsService) // 用户认证处理
6158
// 如果上面那个 userDetailsService 够简单其实可以像下面这样用 SQL 语句查询比对
6259
// .dataSource(dataSource)
6360
// .usersByUsernameQuery("Select * from users where username=?")
6461
// 这个 passwordEncoder 配置的实际就是 DaoAuthenticationConfigurer 的加密器
65-
.passwordEncoder(passwordEncoder());
62+
.passwordEncoder(passwordEncoder()); // 密码处理
6663

6764
}
6865

66+
6967
@Override
7068
protected void configure(HttpSecurity http) throws Exception {
7169
List<String> permitAll = authIgnoreConfig.getIgnoreUrls();
7270
permitAll.add(SysConstant.LOGIN_URL);
71+
permitAll.add(SysConstant.LOGIN_PROCESS);
7372
permitAll.add(SysConstant.LOGOUT_URL);
7473
permitAll.add(SysConstant.JWT_AUTH);
7574
permitAll.add("/static/images/**");
@@ -93,12 +92,17 @@ protected void configure(HttpSecurity http) throws Exception {
9392

9493
// http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
9594

95+
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
96+
9697
http.formLogin()
9798
.loginPage(SysConstant.LOGIN_URL)
98-
// .loginProcessingUrl(SysConstant.LOGIN_PROCESS)
99+
.loginProcessingUrl(SysConstant.LOGIN_PROCESS)
99100
.successHandler(authenticationSuccessHandler())
100101
.failureHandler(customSimpleUrlAuthenticationFailureHandler());
101102

103+
// .defaultSuccessUrl("/index")
104+
// .failureUrl("/login");
105+
102106
http.logout()
103107
.logoutSuccessHandler(customLogoutSuccessHandler())
104108
.permitAll();
@@ -110,7 +114,6 @@ protected void configure(HttpSecurity http) throws Exception {
110114
http.csrf().disable();
111115

112116
// 如果不用验证码,注释这个过滤器即可
113-
// http.addFilterBefore(new ValidateCodeFilter(), UsernamePasswordAuthenticationFilter.class);
114117
// http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
115118

116119

@@ -172,20 +175,5 @@ public AuthenticationFailureHandler customSimpleUrlAuthenticationFailureHandler(
172175
return customSimpleUrlAuthenticationFailureHandler;
173176
}
174177

175-
public AuthenticationFailureHandler exceptionMappingAuthenticationFailureHandler() {
176-
ExceptionMappingAuthenticationFailureHandler exceptionMappingAuthenticationFailureHandle = new ExceptionMappingAuthenticationFailureHandler();
177-
exceptionMappingAuthenticationFailureHandle.setDefaultFailureUrl(SysConstant.LOGIN_URL);
178-
exceptionMappingAuthenticationFailureHandle.setExceptionMappings(buildExceptionMappings());
179-
return exceptionMappingAuthenticationFailureHandle;
180-
}
181-
182-
private Map<String, String> buildExceptionMappings() {
183-
Map<String, String> urlMappings = new HashMap<>();
184-
urlMappings.put(BadCredentialsException.class.getName(), "/login_fail?error=" + LoginError.BADCREDENTIALS.getType());
185-
urlMappings.put(LockedException.class.getName(), "/login_fail?error=" + LoginError.LOCKED.getType());
186-
urlMappings.put(AccountExpiredException.class.getName(), "/login_fail?error=" + LoginError.ACCOUNTEXPIRED.getType());
187-
urlMappings.put(UsernameNotFoundException.class.getName(), "/login_fail?error=" + LoginError.USERNAMENOTFOUND.getType());
188-
return urlMappings;
189-
}
190178

191179
}

src/main/java/top/whgojp/security/detail/CustomUserDetailsService.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
package top.whgojp.security.detail;
22

3-
import cn.hutool.core.collection.CollUtil;
43
import cn.hutool.core.util.ObjectUtil;
54
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
65
import lombok.extern.slf4j.Slf4j;
76
import org.springframework.beans.factory.annotation.Autowired;
8-
import org.springframework.security.core.GrantedAuthority;
9-
import org.springframework.security.core.authority.AuthorityUtils;
107
import org.springframework.security.core.userdetails.UserDetails;
118
import org.springframework.security.core.userdetails.UserDetailsService;
129
import org.springframework.security.core.userdetails.UsernameNotFoundException;
1310
import org.springframework.stereotype.Component;
1411
import top.whgojp.modules.system.entity.User;
1512
import top.whgojp.modules.system.service.UserService;
13+
import top.whgojp.security.handler.CustomSimpleUrlAuthenticationFailureHandler;
1614

1715
import java.util.*;
1816

@@ -27,12 +25,12 @@ public class CustomUserDetailsService implements UserDetailsService {
2725

2826
@Autowired
2927
private UserService userService;
30-
28+
@Autowired
29+
private CustomSimpleUrlAuthenticationFailureHandler customSimpleUrlAuthenticationFailureHandler;
3130
@Override
3231
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
3332
User sysUser = userService.getOne(Wrappers.<User>query().lambda().eq(User::getUsername, username));
3433
if (ObjectUtil.isNull(sysUser)) {
35-
log.info("用户名不存在!");
3634
throw new UsernameNotFoundException("用户不存在");
3735
}
3836

src/main/java/top/whgojp/security/handler/CustomSimpleUrlAuthenticationFailureHandler.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.springframework.security.core.AuthenticationException;
1010
import org.springframework.security.core.userdetails.UsernameNotFoundException;
1111
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
12+
import org.springframework.stereotype.Component;
1213
import org.springframework.util.StringUtils;
1314
import top.whgojp.common.constant.SysConstant;
1415
import top.whgojp.common.enums.LoginError;
@@ -21,6 +22,7 @@
2122

2223
@Data
2324
@Slf4j
25+
@Component
2426
public class CustomSimpleUrlAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
2527

2628
private static final String DEFAULT_FAILURE_URL = SysConstant.LOGIN_URL;
@@ -34,6 +36,7 @@ public class CustomSimpleUrlAuthenticationFailureHandler extends SimpleUrlAuthen
3436
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
3537
super.onAuthenticationFailure(request, response, exception);
3638
setDefaultFailureUrl(determineFailureUrl(exception));
39+
log.info("当前异常:"+exception.getMessage());
3740

3841
String loginIp = request.getRemoteHost();
3942
String loginDate = DateUtil.now();
@@ -48,7 +51,9 @@ public void onAuthenticationFailure(HttpServletRequest request, HttpServletRespo
4851
log.error(ex.getMessage(), ex);
4952
}
5053
}
54+
public void CustomOnAuthenticationFailure(Exception exception){
5155

56+
}
5257
private String determineFailureUrl(AuthenticationException exception) {
5358
// 默认设置登录错误页面为/login
5459
defaultFailureUrl = StringUtils.hasLength(defaultFailureUrl) ? defaultFailureUrl : DEFAULT_FAILURE_URL;
@@ -63,7 +68,13 @@ private String determineFailureUrl(AuthenticationException exception) {
6368
}
6469

6570
private LoginError determineFailureType(AuthenticationException exception) {
66-
if (exception instanceof UsernameNotFoundException) {
71+
if (exception.getMessage() == "验证码为空"){
72+
return LoginError.CAPTCHANOTFOUND;
73+
} else if (exception.getMessage() == "验证码过期") {
74+
return LoginError.CAPTCHAEXPIRED;
75+
} else if (exception.getMessage() == "验证码不正确") {
76+
return LoginError.CAPTCHAERROR;
77+
} else if (exception instanceof UsernameNotFoundException) {
6778
return LoginError.USERNAMENOTFOUND;
6879
} else if (exception instanceof LockedException) {
6980
return LoginError.LOCKED;

src/main/resources/templates/login.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ <h1 class="admin-header">
202202
</div>
203203

204204

205-
<form class="layui-form-div" th:action="@{/login}" method="post">
205+
<form class="layui-form-div" th:action="@{/loginProcess}" method="post">
206206
<div class="item">
207207
<span class="iconfont icon-icon_username" style="font-size: 25px"></span>
208208
<input type="text" name="username" required placeholder="请输入用户名">
@@ -215,7 +215,7 @@ <h1 class="admin-header">
215215
<div class="item">
216216
<span class="iconfont icon-yanzhengyanzhengma" style="font-size: 20px">
217217

218-
<input type="text" name="captcha" placeholder="请输入验证码" autocomplete="off"
218+
<input type="text" name="captcha" required placeholder="请输入验证码" autocomplete="off"
219219
style="padding-left: 5px">
220220
<img id="captcha" class="admin-captcha" width="90" height="30" th:src="@{/captcha}"
221221
onclick="this.src='/captcha?' + Math.random()">

0 commit comments

Comments
 (0)