org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert val

本文介绍了在Spring MVC中遇到前端使用html date控件,后端使用Date类型接收导致的`MethodArgumentTypeMismatchException`异常。异常原因是无法将String类型自动转换为Date类型。解决方案是后端改为使用String类型字段接收,再通过`SimpleDateFormat`的parse方法手动转换为Date类型。

转载请注明出处:https://blog.csdn.net/weixin_41459547/article/details/88843702

若有不对之处,感谢各位批评指导,希望大家一起讨论学习。

 

导读:第一部分为异常详情,第二部分为解决方法,若不想查看异常信息,可直接查看解决方法解决问题,

本文的背景:前端使用html的date控件,在后端使用Date类型字段接收时,发出错误,而String类型字段接收可以实现。具体实现方法为——通过SimpleDateFormat的parse方法将String类型转换为Date类型。

 

异常详情:

因异常代码量过长,所以放在文末,这里先贴出报错的那一整行的信息:

org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type [java.lang.String] to required type [java.util.Date]; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value '2000-09-24'; nested exception is java.lang.IllegalArgumentException

造成错误的原因在异常中已经说出来了,就是无法用Date类型数据接收String类型的数据,即无法发生转换,错误关键语句如下:
 

Failed to convert from type [java.lang.String] to type [java.util.Date]

然后,在发生类型转换的位置看下代码:

前端页面接收的部分,如下图,这里我们使用的类型type=date,所以我们接收的是一个date类型的数据:

<tr>
	<td>注册时间</td>
	<td><input id="jointimeTemp" name="jointimeTemp" type="date" value="2000-09-24"/></td>
</tr>

在后台,我的jointimeTemp字段是Date类型的数据,所以前端才设置为type=date类型。

然后我们在后台接收的方法头部分,我们接收jointimeTemp的类型为Date类型,想着跟前端部分的date类型对应

@RequestMapping("/managerAddStudent.action")
public @ResponseBody String managerAddStudent(Student student,Date jointimeTemp) {
    /*
        省略具体代码及返回值
    */
}

于是,这样配置之后,就爆出了Failed to convert from type [java.lang.String] to type [java.util.Date]的异常。

 

解决方法:

使用后端部分使用String类型字段接收前端的date类型的数据,代码如下;

@RequestMapping("/managerAddStudent.action")
public @ResponseBody String managerAddStudent(Student student,String jointimeTemp) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
    Date date = sdf.parse(jointimeTemp);   
    //SimpleDateFormat的parse方法是返回java.util.Date类型的数据
}

这样,就实现了使用html的date控件,然后在后端通过接收String类型字段,通过SimpleDateFormat的parse方法将String类型转换为Date类型。

package com.kucun.Config; import java.io.IOException; import java.io.InputStream; import java.util.Map; import javax.json.Json; import javax.servlet.Filter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.fasterxml.jackson.databind.ObjectMapper; import com.kucun.Config.user.CustomUserDetails; // 2. 基础安全配置 @Configuration @EnableWebSecurity // 启用Web安全功能 public class SecurityConfig extends WebSecurityConfigurerAdapter{ /** * 核心安全过滤器链配置 * @param http HTTP安全构建器 * @return 安全过滤器链 * @throws Exception 配置异常 * * █ 配置逻辑说明: * 1. authorizeHttpRequests: 定义访问控制规则 * 2. formLogin: 配置表单登录 * 3. logout: 配置注销行为 * 4. exceptionHandling: 处理权限异常[^3] */ // 修正后的配置方法 @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .addFilterBefore(jsonAuthFilter(), UsernamePasswordAuthenticationFilter.class) // 关键配置 .authorizeRequests() .antMatchers(&quot;/login.html&quot;).permitAll() .antMatchers(HttpMethod.POST, &quot;/users/login&quot;).permitAll() .antMatchers(&quot;/users/guanli/**&quot;).hasAuthority(&quot;ROLE_ADMIN&quot;) .antMatchers(&quot;/js/**&quot;, &quot;/css/**&quot;, &quot;/fonts/**&quot;, &quot;/images/**&quot;,&quot;/*&quot;).permitAll() .anyRequest().authenticated() .and() .formLogin().disable() // .loginPage(&quot;/login.html&quot;) // .loginProcessingUrl(&quot;/users/login&quot;) // // .successHandler(ajaxAuthenticationSuccessHandler()) // 自定义成功处理器 // .failureHandler(ajaxAuthenticationFailureHandler()) // 自定义失败处理器 // .defaultSuccessUrl(&quot;/index.html&quot;) // .failureUrl(&quot;/login.html?error=true&quot;) // .usernameParameter(&quot;andy&quot;) // 修改用户名参数名 // .passwordParameter(&quot;pass&quot;) // 修改密码参数名 // .and() .logout() .logoutUrl(&quot;/logout&quot;) .logoutSuccessUrl(&quot;/login.html&quot;) .and() .csrf() .ignoringAntMatchers(&quot;/users/login&quot;) .and() .headers() .frameOptions().sameOrigin() .and() .exceptionHandling() .accessDeniedHandler(accessDeniedHandler()); // 统一使用Handler } // 返回JSON格式的成功响应 @Bean public AuthenticationSuccessHandler ajaxAuthenticationSuccessHandler() { return (request, response, authentication) -> { response.setContentType(MediaType.APPLICATION_JSON_VALUE); CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal(); response.setStatus(HttpStatus.OK.value()); System.out.println(authentication.getPrincipal()+&quot;&quot;+authentication.getName()); if (request.getHeader(&quot;X-Requested-With&quot;) == null) { // 非AJAX请求 response.sendRedirect(&quot;/index.html&quot;); } else { //String re=userDetails.getUser().toString() new ObjectMapper().writeValue(response.getWriter(), userDetails.getUser() ); } }; } // 返回401状态码和错误信息 @Bean public AuthenticationFailureHandler ajaxAuthenticationFailureHandler() { return (request, response, exception) -> { if (request.getHeader(&quot;X-Requested-With&quot;) == null) { response.sendRedirect(&quot;/login.html?error=true&quot;); } else { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.getWriter().write(&quot;{\&quot;error\&quot;:\&quot;Authentication failed\&quot;}&quot;); } }; } // 处理未认证请求 @Bean public AuthenticationEntryPoint ajaxAuthenticationEntryPoint() { return (request, response, exception) -> { if (request.getHeader(&quot;X-Requested-With&quot;) == null) { response.sendRedirect(&quot;/login.html?error=true&quot;); } else { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.getWriter().write(&quot;{\&quot;error\&quot;:\&quot;Authentication failed\&quot;}&quot;); } }; } @Bean public JsonUsernamePasswordAuthenticationFilter jsonAuthFilter() throws Exception { JsonUsernamePasswordAuthenticationFilter filter = new JsonUsernamePasswordAuthenticationFilter(); filter.setAuthenticationManager(authenticationManagerBean()); filter.setUsernameParameter(&quot;andy&quot;); // 设置自定义参数名 filter.setPasswordParameter(&quot;pass&quot;); filter.setFilterProcessesUrl(&quot;/users/login&quot;); filter.setAuthenticationSuccessHandler(ajaxAuthenticationSuccessHandler()); filter.setAuthenticationFailureHandler(ajaxAuthenticationFailureHandler()); return filter; } /** * 密码编码器(必须配置) * 使用BCrypt强哈希算法加密 */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AccessDeniedHandler accessDeniedHandler() { System.out.println(&quot;0000&quot;); return (request, response, ex) -> { if (!response.isCommitted()) { response.sendRedirect(&quot;/error/403&quot;); } }; } } class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private final ObjectMapper objectMapper = new ObjectMapper(); @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { System.out.println(&quot;收到认证请求,路径:&quot; + request.getRequestURI()); System.out.println(&quot;请求方法:&quot; + request.getMethod()); System.out.println(&quot;Content-Type&quot; + request.getContentType()); if (request.getContentType() != null && request.getContentType().startsWith(MediaType.APPLICATION_JSON_VALUE)) { try (InputStream is = request.getInputStream()) { Map<String, String> authMap = objectMapper.readValue(is, Map.class); String username = authMap.getOrDefault(getUsernameParameter(), &quot;&quot;); String password = authMap.getOrDefault(getPasswordParameter(), &quot;&quot;); // 调试日志 System.out.println(&quot;Authentication attempt with: &quot; + username+'_'+ password); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } catch (IOException e) { throw new AuthenticationServiceException(&quot;认证请求解析失败&quot;, e); } } return super.attemptAuthentication(request, response); } } package com.kucun.Config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Service; import com.kucun.Config.Role.RoleConverter; import com.kucun.Config.user.CustomUserDetails; import com.kucun.data.entity.User; import com.kucun.dataDo.UserRepository; /** * 获取数据 * @author Administrator * */ @Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Autowired private RoleConverter roleConverter; public CustomUserDetailsService() { super(); System.out.println(&quot;11111&quot;); } /** * 获取数据库中用户信息 * @param andy 账号 * * @return * */ @Override public UserDetails loadUserByUsername(String andy) { System.out.println(andy); User user = userRepository.findByAndy(andy); System.out.println(user); return new CustomUserDetails(user, roleConverter.convert(user.getRole()) // 关键转换点[^1] ); } }package com.kucun.Config.user; import java.util.Collection; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import com.kucun.data.entity.User; public class CustomUserDetails implements UserDetails { /** * */ private static final long serialVersionUID = 1L; private final String andy; // 对应andy字段 private final String name; private final int role; private final String password; private final User users; private final Collection<? extends GrantedAuthority> authorities; public CustomUserDetails(User user, Collection<? extends GrantedAuthority> authorities) { this.andy = user.getAndy(); this.name = user.getName(); this.role = user.getRole(); this.password = user.getPass(); user.setPass(null); this.users=user; this.authorities = authorities; } // 实现UserDetails接口方法 @Override public String getUsername() { return andy; } @Override public String getPassword() { return password; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } // 自定义字段访问方法 public String getName() { return name; } public User getUser() { return users; } public int getRole() { return role; } // 其他必要方法 @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } index.js function checkLoginStatus() { if(window.location.href== '/KuCun2/login.html')return; const username = localStorage.getItem(&quot;name&quot;); if(!username) window.location.href = '/KuCun2/login.html?11212121'; } login.js // 登录按钮点击事件 function login(datas) { var data={} datas.each(function(a,element){ alert() data[$(element).attr('name')]=$(element).val() }) //var data={ andy,pass } // 模拟 AJAX 请求 https(&quot;/KuCun2/users/login&quot;,data,function (response) { alert(&quot;1&quot;); if (response.name) { localStorage.setItem(&quot;name&quot;, response.name); // 保存用户名到本地存储 localStorage.setItem(&quot;role&quot;, response.role); // 保存权限到本地存储 window.location.href = '/KuCun2/index.html'; } else { alert(&quot;登录失败,请检查用户名和密码!&quot;); } }) };登陆之后跳转index.html又会被重定向登录界面
05-30
<think>好的,我现在需要解决用户描述的登录后跳转回登录页面的问题。首先,用户提供的代码涉及SpringSecurity配置、自定义过滤器以及前端JavaScript代码。我需要仔细分析可能的原因。首先,用户提到在登录成功后,会被重定向到index.html,但随后又被重定向回登录页面。这可能与会话管理或安全配置有关。查看后端代码,发现SecurityConfig中配置了.sessionCreationPolicy(SessionCreationPolicy.STATELESS),这意味着服务端不会创建会话,依赖每次请求携带凭证(如JWT)。但用户的前端代码使用localStorage存储用户名和角色,而非Token,这可能导致后续请求无认证信息,触发重定向。其次,前端checkLoginStatus函数检查localStorage中的name字段是否存在。如果不存在,则跳转登录页。但登录成功后,后端返回的用户数据可能未正确设置到localStorage,或前端代码存在错误,导致name未被存储。例如,login.js中的响应处理可能没有正确获取response.name,或者后端返回的JSON结构不匹配。再看后端的认证成功处理程序ajaxAuthenticationSuccessHandler,当请求是AJAX时,会返回用户信息的JSON。但用户的前端代码可能未正确识别AJAX请求,导致非AJAX处理分支被触发,执行response.sendRedirect(&quot;/index.html&quot;),而前端可能未正确处理重定向,导致循环。 另外,SecurityConfig中配置了.antMatchers(&quot;/js/**&quot;,&quot;/css/**&quot;,&quot;/fonts/**&quot;,&quot;/images/**&quot;,&quot;/*&quot;).permitAll(),这里的&quot;/*&quot;可能错误地允许了所有根路径下的请求,导致未认证用户也能访问index.html,但后续的API请求被拦截,引发重定向。还需要检查自定义的JsonUsernamePasswordAuthenticationFilter是否正确处理了认证请求,确保认证成功后生成并返回了正确的凭据,如JWT或会话Cookie,以便后续请求携带。当前配置是STATELESS,但未使用JWT,可能导致认证状态无法保持。解决方案可能包括:修正会话管理策略,确保生成会话;检查前端存储逻辑,确保正确保存凭证;调整安全配置的路径匹配;以及验证AJAX请求的处理是否正确。此外,后端返回的用户数据字段是否与前端期望的name和role一致也需要确认。</think>根据代码分析,登录后跳转失败的根本原因是**会话管理策略与前端认证状态维护方式不匹配**。以下是具体解决方案: ### 1. 会话管理配置修正 ```java // 修改SecurityConfig的session配置 @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() // 改为有状态会话(原配置为STATELESS导致无会话) .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .invalidSessionUrl(&quot;/login.html?session=invalid&quot;) .maximumSessions(1) .maxSessionsPreventsLogin(false) .and() // ...其他配置保持不变 } ``` ### 2. 增强认证成功处理器 ```java @Bean public AuthenticationSuccessHandler ajaxAuthenticationSuccessHandler() { return (request, response, authentication) -> { // 强制创建服务端会话 request.getSession(true); // 构建安全响应数据 Map<String, Object> responseData = new HashMap<>(); responseData.put(&quot;sessionId&quot;, request.getSession().getId()); responseData.put(&quot;userInfo&quot;, Map.of( &quot;displayName&quot;, ((CustomUserDetails)authentication.getPrincipal()).getName(), &quot;roleLevel&quot;, ((CustomUserDetails)authentication.getPrincipal()).getRole() )); // 统一返回JSON格式 response.setContentType(MediaType.APPLICATION_JSON_VALUE); new ObjectMapper().writeValue(response.getWriter(), responseData); }; } ``` ### 3. 前端状态检查优化 ```javascript // 修改index.js的检查逻辑 function checkLoginStatus() { // 允许直接访问的页面白名单 const publicPages = ['/login.html', '/error/403']; if (publicPages.some(page => window.location.pathname.includes(page))) return; // 双重验证机制 const hasLocalStorage = !!localStorage.getItem('name'); const hasSessionCookie = document.cookie.includes('JSESSIONID='); if (!hasLocalStorage || !hasSessionCookie) { window.location.href = '/KuCun2/login.html?auth=required'; } } ``` ### 问题根源分析 1. **会话策略冲突**: - 原配置`SessionCreationPolicy.STATELESS`禁用服务端会话 - 前端依赖localStorage存储状态但无Token验证机制 - 导致每次请求都被视为未认证 2. **路径匹配错误**: ```java // 修正前(错误配置) .antMatchers(&quot;/*&quot;).permitAll() // 匹配根路径下单级目录 // 修正后(正确配置) .antMatchers(&quot;/**&quot;).permitAll() // 正确匹配多级目录 ``` 3. **AJAX检测逻辑缺陷**: ```java // 原检测方式易被绕过 if (request.getHeader(&quot;X-Requested-With&quot;) == null) // 应改用标准检测方式 if (&quot;XMLHttpRequest&quot;.equals(request.getHeader(&quot;X-Requested-With&quot;))) ``` ### 完整解决方案流程 1. **服务端会话管理**: - 启用有状态会话(SessionCreationPolicy.IF_REQUIRED) - 设置会话超时时间(30分钟) - 配置并发会话控制 2. **前端状态同步**: - 在localStorage存储基础用户信息 - 同步存储服务端返回的session ID - 每次请求携带session cookie 3. **安全响应增强**: ```java // 在成功处理器添加安全头 response.setHeader(&quot;X-Frame-Options&quot;, &quot;DENY&quot;); response.setHeader(&quot;Cache-Control&quot;, &quot;no-cache, no-store&quot;); response.setHeader(&quot;Pragma&quot;, &quot;no-cache&quot;); ``` ---
package com.kucun.Config; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.json.Json; import javax.servlet.Filter; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.databind.ObjectMapper; import com.kucun.Config.user.CustomUserDetails; // 2. 基础安全配置 @Configuration @EnableWebSecurity // 启用Web安全功能 public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Override public void configure(WebSecurity web) { web.ignoring().antMatchers(&quot;/check-session&quot;); } // 添加自定义Controller @RestController public static class SessionCheckController { @GetMapping(&quot;/check-session&quot;) public ResponseEntity<?> checkSession(HttpServletRequest request) { return request.getSession(false) != null ? ResponseEntity.ok().build() : ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } } /** * 核心安全过滤器链配置 * @param http HTTP安全构建器 * @return 安全过滤器链 * @throws Exception 配置异常 * * █ 配置逻辑说明: * 1. authorizeHttpRequests: 定义访问控制规则 * 2. formLogin: 配置表单登录 * 3. logout: 配置注销行为 * 4. exceptionHandling: 处理权限异常[^3] */ // 修正后的配置方法 @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.ALWAYS) .invalidSessionUrl(&quot;/login.html?session=invalid&quot;) .maximumSessions(1) .maxSessionsPreventsLogin(false) .and() .and() .addFilterBefore(jsonAuthFilter(), UsernamePasswordAuthenticationFilter.class) // 关键配置 .authorizeRequests() .antMatchers(&quot;/login.html&quot;, &quot;/users/login&quot;).permitAll() .antMatchers(&quot;/js/**&quot;, &quot;/css/**&quot;, &quot;/fonts/**&quot;, &quot;/images/**&quot;,&quot;/check-session&quot;,&quot;/main/bootstrap-3.3.7-dist/**&quot;).permitAll() .antMatchers(&quot;/users/guanli/**&quot;).hasAuthority(&quot;ROLE_ADMIN&quot;) .anyRequest().authenticated() .and() .formLogin().disable() // .loginPage(&quot;/login.html&quot;) // .loginProcessingUrl(&quot;/users/login&quot;) // // .successHandler(ajaxAuthenticationSuccessHandler()) // 自定义成功处理器 // .failureHandler(ajaxAuthenticationFailureHandler()) // 自定义失败处理器 // .defaultSuccessUrl(&quot;/index.html&quot;) // .failureUrl(&quot;/login.html?error=true&quot;) // .usernameParameter(&quot;andy&quot;) // 修改用户名参数名 // .passwordParameter(&quot;pass&quot;) // 修改密码参数名 // .and() .logout() .logoutUrl(&quot;/logout&quot;) .logoutSuccessUrl(&quot;/login.html&quot;) .and() .csrf() .ignoringAntMatchers(&quot;/users/login&quot;) .and() .headers() .frameOptions().sameOrigin() .and() .exceptionHandling() .accessDeniedHandler(accessDeniedHandler()); // 统一使用Handler } // 返回JSON格式的成功响应 @Bean public AuthenticationSuccessHandler ajaxAuthenticationSuccessHandler() { return (request, response, authentication) -> { // 强制创建服务端会话 request.getSession(true); String contextPath = request.getContextPath(); HttpSession session = request.getSession(true); Cookie cookie = new Cookie(&quot;JSESSIONID&quot;, session.getId()); cookie.setPath(contextPath.isEmpty() ? &quot;/&quot; : contextPath + &quot;/&quot;); cookie.setMaxAge(1800); // 30分钟 response.addCookie(cookie); //构建安全响应数据 Map<String, Object> responseData = new HashMap<>(); responseData.put(&quot;sessionId&quot;, request.getSession().getId()); responseData.put(&quot;userInfo&quot;,Collections.unmodifiableMap(new HashMap<String, Object>() {/** * */ private static final long serialVersionUID = 1L; { put(&quot;Name&quot;, ((CustomUserDetails)authentication.getPrincipal()).getName()); put(&quot;role&quot;, ((CustomUserDetails)authentication.getPrincipal()).getRole()); }})); // 统一返回JSON格式 response.setContentType(MediaType.APPLICATION_JSON_VALUE); // new ObjectMapper().writeValue(response.getWriter(), responseData); response.setContentType(MediaType.APPLICATION_JSON_VALUE); CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal(); response.setStatus(HttpStatus.OK.value()); System.out.println(authentication.getPrincipal()+&quot;&quot;+authentication.getName()); if (request.getHeader(&quot;X-Requested-With&quot;) == null) { // 非AJAX请求 response.sendRedirect(&quot;/index.html&quot;); } else { //String re=userDetails.getUser().toString() new ObjectMapper().writeValue(response.getWriter(), userDetails.getUser() ); } }; } // 返回401状态码和错误信息 @Bean public AuthenticationFailureHandler ajaxAuthenticationFailureHandler() { return (request, response, exception) -> { if (request.getHeader(&quot;X-Requested-With&quot;) == null) { response.sendRedirect(&quot;/login.html?error=true&quot;); } else { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.getWriter().write(&quot;{\&quot;error\&quot;:\&quot;Authentication failed\&quot;}&quot;); } }; } // 处理未认证请求 @Bean public AuthenticationEntryPoint ajaxAuthenticationEntryPoint() { return (request, response, exception) -> { if (request.getHeader(&quot;X-Requested-With&quot;) == null) { response.sendRedirect(&quot;/login.html?error=true&quot;); } else { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.getWriter().write(&quot;{\&quot;error\&quot;:\&quot;Authentication failed\&quot;}&quot;); } }; } @Bean public JsonUsernamePasswordAuthenticationFilter jsonAuthFilter() throws Exception { JsonUsernamePasswordAuthenticationFilter filter = new JsonUsernamePasswordAuthenticationFilter(); filter.setAuthenticationManager(authenticationManagerBean()); filter.setUsernameParameter(&quot;andy&quot;); // 设置自定义参数名 filter.setPasswordParameter(&quot;pass&quot;); filter.setFilterProcessesUrl(&quot;/users/login&quot;); filter.setAuthenticationSuccessHandler(ajaxAuthenticationSuccessHandler()); filter.setAuthenticationFailureHandler(ajaxAuthenticationFailureHandler()); return filter; } /** * 密码编码器(必须配置) * 使用BCrypt强哈希算法加密 */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AccessDeniedHandler accessDeniedHandler() { System.out.println(&quot;0000&quot;); return (request, response, ex) -> { if (!response.isCommitted()) { response.sendRedirect(&quot;/error/403&quot;); } }; } } class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private final ObjectMapper objectMapper = new ObjectMapper(); @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { System.out.println(&quot;收到认证请求,路径:&quot; + request.getRequestURI()); System.out.println(&quot;请求方法:&quot; + request.getMethod()); System.out.println(&quot;Content-Type&quot; + request.getContentType()); if (request.getContentType() != null && request.getContentType().startsWith(MediaType.APPLICATION_JSON_VALUE)) { try (InputStream is = request.getInputStream()) { Map<String, String> authMap = objectMapper.readValue(is, Map.class); String username = authMap.getOrDefault(getUsernameParameter(), &quot;&quot;); String password = authMap.getOrDefault(getPasswordParameter(), &quot;&quot;); // 调试日志 System.out.println(&quot;Authentication attempt with: &quot; + username+'_'+ password); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } catch (IOException e) { throw new AuthenticationServiceException(&quot;认证请求解析失败&quot;, e); } } Authentication aut= super.attemptAuthentication(request, response); System.out.println(&quot;结果:&quot;+aut.isAuthenticated()); return aut; } } package com.kucun.Config.Role; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.PostConstruct; import javax.json.Json; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Component; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; /** * 权限转化 * @author Administrator * */ @Component public class RoleConverter { private static final Map<Integer, String> ROLE_MAP = new HashMap<>(); @PostConstruct public void init() { ROLE_MAP.put(0, &quot;ROLE_ADMIN&quot;); ROLE_MAP.put(1, &quot;ROLE_USER&quot;); ROLE_MAP.put(2, &quot;ROLE_MANAGER&quot;); ROLE_MAP.put(3, &quot;ROLE_AUDITOR&quot;); } public List<GrantedAuthority> convert(int roleCode) { ObjectMapper mapper = new ObjectMapper(); try { System.out.println(mapper.writeValueAsString(Collections.singletonList( new SimpleGrantedAuthority(ROLE_MAP.getOrDefault(roleCode, &quot;ROLE_GUEST&quot;)))).toString());//输出[{&quot;authority&quot;:&quot;ROLE_ADMIN&quot;}] } catch (JsonProcessingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return Collections.singletonList( new SimpleGrantedAuthority(ROLE_MAP.getOrDefault(roleCode, &quot;ROLE_GUEST&quot;)) ); } } function https(url,data,fu){ $.ajax({ contentType:&quot;application/json&quot;, url: url, // 假设后端 API 地址 method: &quot;POST&quot;, data: JSON.stringify(data), type: &quot;json&quot;, success:fu, error: function (e) { console.error(e) alert(&quot;请求失败,请稍后再试!&quot;+e); } }); } function checkLoginStatus() { if (window.location.href.includes('/login.html')) return; const username = localStorage.getItem(&quot;name&quot;); if (!username) { window.location.href = '/KuCun2/login.html'; alert(&quot;meijin&quot;) return; } // 检查会话是否有效 fetch('/KuCun2/check-session', { headers: { &quot;X-Requested-With&quot;: &quot;XMLHttpRequest&quot;, // 显式添加头 &quot;Content-Type&quot;: &quot;application/json&quot; }, credentials: 'include' // 强制携带 Cookie }).then(response => { if (!response.ok) { localStorage.removeItem(&quot;name&quot;); alert(&quot;meijin2&quot;) window.location.href = '/KuCun2/login.html'; alert(&quot;meijin3&quot;) } }); } function deepMergeArrays(frontend, backend) { const resultMap = new Map(); // 遍历前端数据并存入 Map 中以便快速查找 frontend.forEach(item => resultMap.set(item.id, { ...item })); // 遍历后端数据并与前端数据进行合并 backend.forEach(item => { if (resultMap.has(item.id)) { // 如果存在相同 ID,则合并两者的内容 resultMap.set( item.id, Object.assign(resultMap.get(item.id), item) ); } else { // 如果不存在相同 ID,则新增该条目 resultMap.set(item.id, { ...item }); } }); // 将最终结果转回数组形式 return Array.from(resultMap.values()); } (function ($){ // 页面加载时检查登录状态 checkLoginStatus(); })(jQuery); function removeSpecificCharactersAndConvertToNumber(str, charsToRemove) { const regex = new RegExp(`[${charsToRemove}]`, 'g'); // 创建用于匹配指定字符的正则表达式 const cleanedStr = str.replace(regex, ''); // 移除指定字符 const numberValue = parseFloat(cleanedStr); // 转换为浮点数 return isNaN(numberValue) ? null : numberValue; // 如果无法解析,则返回 null } /// <reference path=&quot;jquery.d.ts&quot; /> // $(document).ready(function(){ // $(&quot;#submit&quot;).click(function(){ // // // $.ajax({ // url:&quot;../users/login&quot;, // async:false, // data:JSON.stringify({ // andy:$(&quot;#andy&quot;).val(), // pass:$(&quot;#pass&quot;).val(), // name:$(&quot;#name&quot;).val()}), // type:&quot;post&quot;, // contentType:&quot;application/json&quot;, // success:function(e){ // alert(e) // } // }); // // // }); // }) (function ($) { &quot;use strict&quot;; /*================================================================== [ Focus Contact2 ]*/ $('.input100').each(function () { $(this).on('blur', function () { if ($(this).val().trim() != &quot;&quot;) { $(this).addClass('has-val'); } else { $(this).removeClass('has-val'); } }) }) /*================================================================== [ Validate ]*/ var input = $('.validate-input .input100'); $('.validate-form').on('submit', function (e) { e.preventDefault(); var check = true; for (var i = 0; i < input.length; i++) { if (validate(input[i]) == false) { showValidate(input[i]); check = false; } } if(check) login(input); return check; }); $('.validate-form .input100').each(function () { $(this).focus(function () { hideValidate(this); }); }); function validate(input) { if ($(input).attr('type') == 'email' || $(input).attr('name') == 'email') { if ($(input).val().trim().match(/^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{1,5}|[0-9]{1,3})(\]?)$/) == null) { return false; } } else { if ($(input).val().trim() == '') { return false; } } } function showValidate(input) { var thisAlert = $(input).parent(); alert(input) $(thisAlert).addClass('alert-validate'); } function hideValidate(input) { var thisAlert = $(input).parent(); $(thisAlert).removeClass('alert-validate'); } // 登录按钮点击事件 function login(datas) { var data={} datas.each(function(a,element){ alert() data[$(element).attr('name')]=$(element).val() }) //var data={ andy,pass } // 模拟 AJAX 请求 https(&quot;/KuCun2/users/login&quot;,data,function (response) { alert(&quot;1&quot;); if (response.name) { localStorage.setItem(&quot;name&quot;, response.name); // 保存用户名到本地存储 localStorage.setItem(&quot;role&quot;, response.role); // 保存权限到本地存储 alert( response) //window.location.href = '/KuCun2/index.html'; } else { alert(&quot;登录失败,请检查用户名和密码!&quot;); } }) }; // 注销按钮点击事件 $(&quot;#logout-btn&quot;).click(function () { localStorage.removeItem(&quot;name&quot;); // 清除本地存储中的用户名 checkLoginStatus(); // 更新登录状态 }); })(jQuery);2025-05-29 16:48:55.354 DEBUG 2708 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/index.html&quot;, parameters={} 2025-05-29 16:48:55.372 DEBUG 2708 --- [nio-8080-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:55.400 DEBUG 2708 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:55.441 DEBUG 2708 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/fonts/font-awesome-4.7.0/css/font-awesome.min.css&quot;, parameters={} 2025-05-29 16:48:55.442 DEBUG 2708 --- [nio-8080-exec-6] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:55.450 DEBUG 2708 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:55.453 DEBUG 2708 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/css/main.css&quot;, parameters={} 2025-05-29 16:48:55.455 DEBUG 2708 --- [nio-8080-exec-6] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:55.457 DEBUG 2708 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/css/util.css&quot;, parameters={} 2025-05-29 16:48:55.459 DEBUG 2708 --- [nio-8080-exec-7] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:55.459 DEBUG 2708 --- [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/css/index2.css&quot;, parameters={} 2025-05-29 16:48:55.460 DEBUG 2708 --- [nio-8080-exec-9] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:55.461 DEBUG 2708 --- [nio-8080-exec-8] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/js/jsyilai.js&quot;, parameters={} 2025-05-29 16:48:55.462 DEBUG 2708 --- [nio-8080-exec-8] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:55.462 DEBUG 2708 --- [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:55.465 DEBUG 2708 --- [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/js/jquery-3.2.1.min.js&quot;, parameters={} 2025-05-29 16:48:55.465 DEBUG 2708 --- [nio-8080-exec-9] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:55.466 DEBUG 2708 --- [nio-8080-exec-8] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:55.473 DEBUG 2708 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:55.506 DEBUG 2708 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:55.509 DEBUG 2708 --- [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:55.619 DEBUG 2708 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/main/index.html&quot;, parameters={} 2025-05-29 16:48:55.621 DEBUG 2708 --- [nio-8080-exec-2] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:55.625 DEBUG 2708 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:55.678 DEBUG 2708 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/js/main.js?1748508535614&_=1748508535584&quot;, parameters={masked} 2025-05-29 16:48:55.681 DEBUG 2708 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:55.696 DEBUG 2708 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:55.711 DEBUG 2708 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/js/index.js?1748508535617&_=1748508535585&quot;, parameters={masked} 2025-05-29 16:48:55.711 DEBUG 2708 --- [nio-8080-exec-4] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:55.714 DEBUG 2708 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:55.769 DEBUG 2708 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/main/bootstrap-3.3.7-dist/js/MyTable.js&quot;, parameters={} 2025-05-29 16:48:55.772 DEBUG 2708 --- [nio-8080-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:55.783 DEBUG 2708 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:55.846 DEBUG 2708 --- [nio-8080-exec-8] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/check-session&quot;, parameters={} 2025-05-29 16:48:55.851 DEBUG 2708 --- [nio-8080-exec-8] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.kucun.Config.SecurityConfig$SessionCheckController#checkSession(HttpServletRequest) 2025-05-29 16:48:55.903 DEBUG 2708 --- [nio-8080-exec-8] o.s.w.s.m.m.a.HttpEntityMethodProcessor : No match for [*/*], supported: [] 2025-05-29 16:48:55.904 DEBUG 2708 --- [nio-8080-exec-8] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:56.053 DEBUG 2708 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/login.html&quot;, parameters={} 2025-05-29 16:48:56.054 DEBUG 2708 --- [nio-8080-exec-6] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:56.057 DEBUG 2708 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:56.177 DEBUG 2708 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/fonts/font-awesome-4.7.0/css/font-awesome.min.css&quot;, parameters={} 2025-05-29 16:48:56.178 DEBUG 2708 --- [io-8080-exec-10] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:56.188 DEBUG 2708 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:56.190 DEBUG 2708 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/css/util.css&quot;, parameters={} 2025-05-29 16:48:56.190 DEBUG 2708 --- [io-8080-exec-10] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:56.204 DEBUG 2708 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:56.206 DEBUG 2708 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/css/main.css&quot;, parameters={} 2025-05-29 16:48:56.206 DEBUG 2708 --- [io-8080-exec-10] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:56.208 DEBUG 2708 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:56.210 DEBUG 2708 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/js/jquery-3.2.1.min.js&quot;, parameters={} 2025-05-29 16:48:56.210 DEBUG 2708 --- [io-8080-exec-10] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:56.218 DEBUG 2708 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:56.220 DEBUG 2708 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/js/jsyilai.js&quot;, parameters={} 2025-05-29 16:48:56.221 DEBUG 2708 --- [io-8080-exec-10] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:56.224 DEBUG 2708 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:56.345 DEBUG 2708 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/images/bg-01.jpg&quot;, parameters={} 2025-05-29 16:48:56.347 DEBUG 2708 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:56.351 DEBUG 2708 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:56.367 DEBUG 2708 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/js/main.js?1748508536335&_=1748508536268&quot;, parameters={masked} 2025-05-29 16:48:56.368 DEBUG 2708 --- [nio-8080-exec-4] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:56.374 DEBUG 2708 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : Completed 200 OK 2025-05-29 16:48:56.509 DEBUG 2708 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : GET &quot;/KuCun2/js/login.js?1748508536338&_=1748508536269&quot;, parameters={masked} 2025-05-29 16:48:56.510 DEBUG 2708 --- [nio-8080-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [&quot;classpath:/jsp/&quot;] 2025-05-29 16:48:56.512 DEBUG 2708 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : Completed 200 OK
05-30
<think>好的,我现在需要分析用户提供的Java代码和相关日志,找出导致登录后仍被重定向到登录页面的原因。首先,我会查看日志中的请求流程,特别是涉及会话检查的部分。用户提到在访问`/check-session`端点时,即使会话存在,也可能返回未授权状态,这可能是因为会话中没有正确的认证信息。接下来,我会检查`SecurityConfig`类的配置。注意到用户配置了`sessionCreationPolicy(SessionCreationPolicy.ALWAYS)`,这应该确保会话被创建,但可能没有正确地将认证信息绑定到会话中。此外,`SessionCheckController`的`checkSession`方法仅检查会话是否存在,而没有验证`SecurityContextHolder`中的认证状态,这可能导致误判。然后,查看`ajaxAuthenticationSuccessHandler`,发现虽然设置了Cookie,但可能没有正确地将认证信息存储到会话中。此外,前端在检查会话时可能没有正确处理AJAX请求的响应,导致错误的重定向。 在`JsonUsernamePasswordAuthenticationFilter`中,认证逻辑是否正确处理了JSON请求,并将认证信息设置到`SecurityContextHolder`中?可能存在认证成功后未正确保存认证信息到会话的问题。另外,注意到前端JavaScript中的`checkLoginStatus`函数在接收到401响应时直接重定向,但可能没有正确处理响应头或会话信息,导致即使认证成功也会被重定向。最后,需要验证会话创建和认证信息绑定的流程,确保每次认证后,`SecurityContext`被正确存储到会话中,并且前端正确携带Cookie进行后续请求。</think>根据代码和日志分析,**登录后无限重定向的根本原因是认证信息未正确绑定到会话**。以下是完整的解决方案: --- ### **问题根源** 1. **会话与认证上下文分离** ```java // SecurityConfig 中未强制绑定 SecurityContext 到会话 SecurityContextHolder.getContext().setAuthentication(authentication); ``` 2. **前端会话检查逻辑缺陷** ```javascript // checkLoginStatus 未处理 200 之外的响应码 if (response.status !== 200) 直接跳转 ``` --- ### **解决方案** #### 1. 修正安全配置(强制绑定认证上下文) ```java @Override protected void configure(HttpSecurity http) throws Exception { http .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.ALWAYS) .sessionFixation().migrateSession() // 关键修复点 .and() // 其他配置保持不变... .addFilterBefore(jsonAuthFilter(), UsernamePasswordAuthenticationFilter.class) .securityContext() .securityContextRepository(new HttpSessionSecurityContextRepository()); // 强制保存上下文到会话 } ``` #### 2. 增强认证成功处理器 ```java @Bean public AuthenticationSuccessHandler ajaxAuthenticationSuccessHandler() { return (request, response, authentication) -> { // 强制保存认证上下文到会话 SecurityContextHolder.getContext().setAuthentication(authentication); request.getSession().setAttribute( HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext() ); // 动态设置 Cookie 路径 String contextPath = request.getContextPath(); Cookie sessionCookie = new Cookie(&quot;JSESSIONID&quot;, request.getSession().getId()); sessionCookie.setPath(contextPath.isEmpty() ? &quot;/&quot; : contextPath); sessionCookie.setHttpOnly(true); response.addCookie(sessionCookie); // 统一响应处理 if (&quot;XMLHttpRequest&quot;.equals(request.getHeader(&quot;X-Requested-With&quot;))) { response.setContentType(MediaType.APPLICATION_JSON_VALUE); new ObjectMapper().writeValue(response.getWriter(), Collections.singletonMap(&quot;redirectUrl&quot;, &quot;/index.html&quot;)); } else { response.sendRedirect(&quot;/index.html&quot;); } }; } ``` #### 3. 修正会话检查端点 ```java @RestController public static class SessionCheckController { @GetMapping(&quot;/check-session&quot;) public ResponseEntity<?> checkSession(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session != null) { SecurityContext context = (SecurityContext) session.getAttribute( HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY ); if (context != null && context.getAuthentication().isAuthenticated()) { return ResponseEntity.ok().header(&quot;X-Session-Valid&quot;, &quot;true&quot;).build(); } } return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } } ``` #### 4. 优化前端会话检查 ```javascript function checkLoginStatus() { fetch('/KuCun2/check-session', { credentials: 'include', headers: { &quot;X-Requested-With&quot;: &quot;XMLHttpRequest&quot;, &quot;Cache-Control&quot;: &quot;no-store&quot; } }).then(response => { const sessionValid = response.headers.get('X-Session-Valid'); if (response.status === 200 && sessionValid === 'true') { return Promise.resolve(); } localStorage.clear(); window.location.href = '/KuCun2/login.html?session=expired'; }).catch(() => { window.location.href = '/KuCun2/login.html?error=network'; }); } ``` --- ### **技术原理说明** | 问题现象 | 技术原理 | 解决方案 | |-------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------| | 会话存在但未认证 | SecurityContext 未存储到 HttpSession | 使用 `HttpSessionSecurityContextRepository` 强制保存上下文 | | AJAX 跳转失效 | 前端未正确解析服务端返回的认证状态 | 在响应头中添加自定义状态标识 `X-Session-Valid` | | 多次重定向循环 | 未清除无效的本地存储数据 | 在跳转登录页前执行 `localStorage.clear()` | --- ### **验证方法** 1. **会话绑定测试** ```java // 添加调试代码 System.out.println(&quot;Session SecurityContext: &quot; + session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY)); ``` 2. **Cookie 路径检查** ```bash curl -I http://localhost:8080/KuCun2/check-session --cookie &quot;JSESSIONID=xxx&quot; ``` --- ### **配置优化点** ```java // 防止静态资源请求创建会话 @Override public void configure(WebSecurity web) { web.ignoring() .antMatchers(&quot;/css/**&quot;, &quot;/js/**&quot;, &quot;/images/**&quot;) .requestMatchers(PathRequest.toStaticResources().atCommonLocations()); } ``` --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值