springSecurity报错java.lang.StackOverflowError: null,原因是ProviderManager.authenticate(Authentication authentication)方法中,provder和toTest一直不匹配,导致循环调用。
两者不匹配导致一直无法进入if中:
在方法中又一次调用authenticate()方法,以此循环导致栈溢出:
我的配置:
package com.rojdar.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* @ClassName: WebSecurityConfig
* @author: ROJDAR
* @CreateDate: 2023/2/7 9:56
* @Description: 配置security第二步:创建securityWEB配置类 用于管理security的拦截路径 放行路径 密码解码器等
**/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Autowired
private UserDetailsService userDetailsService;
/**
* 创建密码编码器
* 在进行认证操作的时候就会根据设置的编码器将传入的密码加密后和数据库保存的密码进行比对
*
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsService(userDetailsService);
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
auth.authenticationProvider(provider);
}
@Override
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManagerBean();
}
/**
* 3.授权规则配置
* - anyRequest():任何请求
* - antMatchers(“/path”) :匹配某个资源路径
* - authenticationed() : 保护URL需要登录访问anyRequest().authenticationed()
* - permitAll():指定url无需保护(放行)一般用户静态资源antMatchers(“/path”).permitAll()
* - hasRole(String role):某个资源需要用户拥有什么样的role才能访问
* 例:antMatchers(“/path”).hasRole(“admin”)
* - hasAuthority**(String authority):某个资源需要用户拥有什么样的权限才能访问
* 例:antMatchers(“/path”).hasAuthority(“admin”)
* - (String ..…roles):某个资源拥有指定角色中的一个就能访问
* - hasAnyAuthority(String ..… authorities):某个资源拥有指定权限中的一个就能访问
* - access(String attribute):该方法使用SPEL表达式,可以创建复杂的限制
* - hasIpAddress(String ip):拥有什么样的ip或子网可以访问该资源
* 权限的设置是按照`从上到下的优先级`。及满足了最开始的权限设置,那么后面的设置就不起作用了
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
/*
匹配到的请求 放行
*/
.antMatchers("/login").anonymous()
/*
所有请求 需要认证 当前请求进来时 检查是否放行是从上到下进行的 上面已经配置了/login放行 哪怕下面配置了
所有请求都需要认证但是/login请求还是会放行
*/
.anyRequest().authenticated()
/*
关闭跨域检查
*/
.and().csrf().disable()
/*
登出 放行
*/
.logout().permitAll();
// 添加JWT filter 到 UsernamePasswordAuthenticationFilter 之前
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
}
package com.rojdar.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.rojdar.domain.TLogin;
import com.rojdar.service.LoginService;
import com.rojdar.utils.AjaxResult;
import com.rojdar.utils.HttpStatus;
import com.rojdar.utils.JwtUtils;
import com.rojdar.utils.RedisOperator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.HashMap;
/**
* @ClassName: LoginServiceImpl
* @author: ROJDAR
* @CreateDate: 2023/2/7 11:07
* @Description: 登录实现
**/
@Service
public class LoginServiceImpl implements LoginService {
//认证管理器
@Resource
private AuthenticationManager authenticationManager;
@Autowired
private RedisOperator redisOperator;
/**
* 自定义实现登录过程
*
* @param user
* @return
*/
@Override
public AjaxResult login(TLogin user) {
/*
* 创建UsernamePasswordAuthenticationToken将用户信息拿去认证管理器认证
*/
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
/*
认证结果返回后 如果认证失败则return 认证成功就创建jwt令牌带回
*/
if (StringUtils.isEmpty(authenticate)) {
return AjaxResult.error("用户名称或者密码错误");
}
TLogin DBUser = (TLogin) authenticate.getPrincipal();
HashMap<String, Object> map = new HashMap<>(1);
map.put(JwtUtils.DETAILS_USER_ID, DBUser.getId());
String token = JwtUtils.createToken(map);
redisOperator.set("login:"+DBUser.getId(), JSONObject.toJSONString(DBUser),60 * 30);
return new AjaxResult(HttpStatus.SUCCESS, "登录成功").put("token", token);
}
}