先参考文章https://blog.csdn.net/grd_java/article/details/107584578,了解springSecurity基本操作,另外,数据库也在这篇文章创建了 |
源码,码云https://gitee.com/yin_zhipeng/spring-security-scaffolding |
文章目录
- 一、环境说明(太基本的就不讲了,本文主要讲如何前后端分离模式整合Security)
- 二、配置Security
-
- 1. 用户实体类继承UserDetails接口
- 2. 编写JWT工具类
- 3. 配置JWT过滤器
- 4. 自定义返回结果
- 5. Security配置类
- 三、实现登录逻辑
-
- 1. 专门给登录用户的实体类
- 2. controller
- 3. service
- 四、通过swagger测试
-
- 1. 配置swagger,携带指定请求头authorization,规定哪些路径需要认证(只有规定的路径才会携带请求头)
- 2. 测试
- 五、验证码实现
-
- 1. 生成验证码
- 2. 修改service逻辑
- 3. 使用redis改造,将验证码存储到redis中
- 六、实现权限管理系统
- 七、解决高版本循环依赖和springboot2.6.x与swagger不兼容问题
- 请求login接口,获取token
- 获取token后,通过封装名为Authorization的请求头,值为Bearer+空格+token字符串的形式请求需要授权接口(就是请求时,需要带上authorization:Bearer eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2Mzk0NjA2MTYsImV4cCI6MTYzOTQ2MTMwMCwic3ViIjoiYWRtaW4iLCJjcmVhdGVkIjoxNjM5NDYwNjE2MTI0fQ._oKG2qarbKi84gxdQjHoRSHd3hx-INQn1CscMgAiASW0B6tsPIjWi1LMr35OTtWR-WvQ8R6tRAvkp0Q3RQm0LQ这样的请求头)
- authorization是请求头key
- Bearer 是我们的一个头标识
- 剩下的就是token字符串
一、环境说明(太基本的就不讲了,本文主要讲如何前后端分离模式整合Security)
common除了Spring security模块外,都在这篇文章中有讲解https://blog.csdn.net/grd_java/article/details/107452826
不知道怎么建表,请到文章开头的链接中参考
二、配置Security
首先,我们在yml中需要配置一些参数,是我们个人规定好的 |
- 我们规定相应的token 请求头为authorization:Bearer token字符串
jwt:
tokenHeader: Authorization
secret: ukc8BDbRigUDaY6pZFfWus2jZWLPHO
expiration: 684808
tokenHead: Bearer
1. 用户实体类继承UserDetails接口
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="DdUser对象,使用Spring Security框架就要继承UserDetails接口,实现方法,将返回值改为true", description="")
public class DdUser implements UserDetails {
private static final long serialVersionUID=1L;
@ApiModelProperty(value = "id")
@TableId(value = "id", type = IdType.ID_WORKER_STR)
private Integer id;
private String username;
private String password;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
2. 编写JWT工具类
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
public class JwtTokenUtil {
private static final String CLAIM_KEY_USERNAME="sub";
private static final String CLAIM_KEY_CREATED="created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public static final long EXPIRE = 1000 * 60 * 60 * 24;
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
public static String getJwtToken(String id, String nickname){
String JwtToken = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.setSubject("guli-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.claim("id", id)
.claim("nickname", nickname)
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
public String getJwtToken(UserDetails userDetails){
Map<String,Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED,new Date());
String JwtToken = gennerateToken(claims);
return JwtToken;
}
private String gennerateToken(Map<String,Object> claims){
String JwtToken =Jwts.builder()
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.addClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
return JwtToken;
}
public String getUsernameByToken(String token){
String username;
try {
Claims claims = getClaimsFormToken(token);
username = claims.getSubject();
}catch (Exception e){
username = null;
e.printStackTrace();
}
return username;
}
private Claims getClaimsFormToken(String token){
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}catch (Exception e){
e.printStackTrace();
}
return claims;
}
private Date getExpiredDateFromToken(String token){
Claims claimsFormToken = getClaimsFormToken(token);
return claimsFormToken.getExpiration();
}
private boolean isTokenExpired(String token){
Date expiredDateFromToken = getExpiredDateFromToken(token);
return expiredDateFromToken.before(new Date());
}
public boolean checkToken(String jwtToken,UserDetails userDetails) {
if(StringUtils.isEmpty(jwtToken)) return false;
String username;
try {
username = getUsernameByToken(jwtToken);
} catch (Exception e) {
e.printStackTrace();
username = null;
return false;
}
if(!username.equals(userDetails.getUsername())) return false;
if(isTokenExpired(jwtToken)) return false;
return true;
}
public boolean canRefresh(String token){
return !isTokenExpired(token);
}
public String refreshToken(String token){
Claims claimsFormToken = getClaimsFormToken(token);
claimsFormToken.put(CLAIM_KEY_CREATED,new Date());
return gennerateToken(claimsFormToken);
}
}
3. 配置JWT过滤器
任何请求都需要进行过滤,如果请求携带我们规定的token,那么将token放在spring Security对象中 |
import com.dd.security.utils.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
public class JwtAuthencationTokenFilter extends OncePerRequestFilter {
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader(tokenHeader);
if(authHeader!=null && authHeader.startsWith(tokenHead)){
String authToken = authHeader.substring(tokenHead.length());
String username = jwtTokenUtil.getUsernameByToken(authToken);
if(username != null && SecurityContextHolder.getContext().getAuthentication() == null){
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if(jwtTokenUtil.checkToken(authToken,userDetails)){
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
filterChain.doFilter(request,response);
}
}
4. 自定义返回结果
import com.dd.common_utils.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Component
public class RestAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter out = response.getWriter();
Result result = Result.error().code(403).message("权限不足,请联系管理员");
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
}
}
当未登录或token失效时访问接口,自定义返回结果 |
import com.dd.common_utils.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Component
public class RestAuthorizationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter out = response.getWriter();
Result result = Result.error().code(401).message("请登录!!!");
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
}
}
5. Security配置类
import com.dd.security.entity.DdUser;
import com.dd.security.exception.RestAccessDeniedHandler;
import com.dd.security.exception.RestAuthorizationEntryPoint;
import com.dd.security.filter.JwtAuthencationTokenFilter;
import com.dd.security.service.DdUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
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;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DdUserService ddUserService;
@Autowired
private RestAccessDeniedHandler restAccessDeniedHandler;
@Autowired
private RestAuthorizationEntryPoint restAuthorizationEntryPoint;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
return username->{
DdUser user = ddUserService.getLoginInfoByUsername(username);
if(user == null){
return null;
}
return user;
};
}
@Bean
public JwtAuthencationTokenFilter jwtAuthencationTokenFilter(){
return new JwtAuthencationTokenFilter();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.headers()
.cacheControl()
;
http.addFilterBefore(jwtAuthencationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling()
.accessDeniedHandler(restAccessDeniedHandler)
.authenticationEntryPoint(restAuthorizationEntryPoint);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/login",
"/logout",
"/css/**",
"/js/**",
"/swagger-ui.html",
"/webjars/**",
"/swagger-resources/**",
"/v2/api-docs/**"
);
}
}
三、实现登录逻辑
1. 专门给登录用户的实体类
2. controller
import com.dd.common_utils.Result;
import com.dd.security.entity.DdUser;
import com.dd.security.entity.UserLogin;
import com.dd.security.service.DdUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
@RestController
@Api(tags = "LoginController,登录")
public class LoginController {
@Autowired
private DdUserService ddUserService;
@ApiOperation(value = "登录之后返回token,如果想要带着token请求,需要添加Bearer前缀,Bearer token,中间空格分隔")
@PostMapping("/login")
public Result login(@RequestBody UserLogin userLogin, HttpServletRequest request){
if(userLogin == null){
Result.error().message("请输入用户名和密码");
}
if(userLogin.getUsername()==null){
Result.error().message("请输入用户名");
}
if(userLogin.getPassword()==null){
Result.error().message("请输入密码");
}
return ddUserService.login(userLogin,request);
}
@ApiOperation(value = "获取当前登录用户信息")
@GetMapping("/login/info")
public Result loginInfo(@ApiParam(value ="全局对象,security将信息设置到全局,通过这个就可以获取")
Principal principal){
if(principal == null){
return Result.error().message("用户信息不存在");
}
String username = principal.getName();
DdUser ddUser = ddUserService.getLoginInfoByUsername(username);
if(ddUser == null){
return Result.error().message("用户信息不存在");
}
ddUser.setPassword(null);
return Result.ok().message("用户信息获取成功").data("loginInfo",ddUser);
}
@ApiOperation(value = "退出登录")
@PostMapping("/logout")
public Result logout(){
return Result.ok().message("退出登录");
}
}
3. service
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dd.common_utils.Result;
import com.dd.security.entity.DdUser;
import com.dd.security.entity.UserLogin;
import com.dd.security.mapper.DdUserMapper;
import com.dd.security.service.DdUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dd.security.utils.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
@Service
public class DdUserServiceImpl extends ServiceImpl<DdUserMapper, DdUser> implements DdUserService {
@Autowired
private DdUserMapper ddUserMapper;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Override
public Result login(UserLogin userLogin, HttpServletRequest request) {
UserDetails userDetails = userDetailsService.loadUserByUsername(userLogin.getUsername());
if(userDetails==null){
return Result.error().message("用户不存在");
}
if(!passwordEncoder.matches(userLogin.getPassword(),userDetails.getPassword())){
return Result.error().message("密码错误");
}
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
String jwtToken = jwtTokenUtil.getJwtToken(userDetails);
return Result.ok().message("登录成功,获取token")
.data("token",jwtToken)
.data("tokenHead",tokenHead);
}
@Override
public DdUser getLoginInfoByUsername(String username) {
DdUser ddUser = ddUserMapper.selectOne(new QueryWrapper<DdUser>().eq("username", username));
if(ddUser == null){
return null;
}
return ddUser;
}
}
四、通过swagger测试
1. 配置swagger,携带指定请求头authorization,规定哪些路径需要认证(只有规定的路径才会携带请求头)
package com.dd.service_base.config;
import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket webApiConfig() {
List<Parameter> pars = new ArrayList<Parameter>();
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
.paths(Predicates.not(PathSelectors.regex("/admin/.*")))
.paths(Predicates.not(PathSelectors.regex("/error/.*")))
.build()
.securityContexts(securityContexts())
.securitySchemes(securitySchemes())
;
}
private ApiInfo webApiInfo() {
return new ApiInfoBuilder()
.title("gulischool 接口 API 文档")
.description("展示先做基础功能,后面再添加业务")
.termsOfServiceUrl("https://www.dd.com/aa/")
.version("1.0")
.contact(new Contact("Helen","http://dd.com","[email protected]"))
.build();
}
private List<ApiKey> securitySchemes(){
ArrayList<ApiKey> result = new ArrayList<>();
ApiKey apiKey = new ApiKey("Authorization", "Authorization", "Header");
result.add(apiKey);
return result;
}
private List<SecurityContext> securityContexts(){
ArrayList<SecurityContext> result = new ArrayList<>();
result.add(getContextByPath("/service_animation/.*"));
result.add(getContextByPath("/login/info"));
result.add(getContextByPath("/logout"));
result.add(getContextByPath("/security/*"));
return result;
}
private SecurityContext getContextByPath(String pathRegex) {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("^(?!auth).*$"))
.build();
}
private List<SecurityReference> defaultAuth() {
ArrayList<SecurityReference> result = new ArrayList<>();
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0]=authorizationScope;
result.add(new SecurityReference("Authorization",authorizationScopes));
return result;
}
}
2. 测试
前端呢,只需要登录后按规则保存token,请求时,携带这个token就可以访问了 |
五、验证码实现
1. 生成验证码
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class CaptchaConfig {
@Bean
public DefaultKaptcha defaultKaptcha(){
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
properties.setProperty("kaptcha.border","yes");
properties.setProperty("kaptcha.border.color","105,179,98");
properties.setProperty("kaptcha.session.key","code");
properties.setProperty("kaptcha.textproducer.font.color","blue");
properties.setProperty("kaptcha.textproducer.font.names","宋体,楷体,微软雅黑");
properties.setProperty("kaptcha.textproducer.font.size","30");
properties.setProperty("kaptcha.textproducer.char.length","4");
properties.setProperty("kaptcha,textproducer.char.space","4");
properties.setProperty("kaptcha.image.width","100");
properties.setProperty("kaptcha.image.height","40");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
import com.google.code.kaptcha.impl.DefaultKaptcha;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
@RestController
public class CaptchaController {
@Autowired
private DefaultKaptcha defaultKaptcha;
@ApiOperation(value = "验证码,以image/jpeg格式固定响应")
@GetMapping(value = "/captcha",produces = "image/jpeg")
public void captcha(HttpServletRequest request, HttpServletResponse response) {
response.setDateHeader("Expires",0);
response.setHeader("Cache-Control","no-store, no-cache, must-revalidate");
response.addHeader("Cache-Control","post-check=0,pre-check=0");
response.setHeader("Pragma","no-cache");
response.setContentType("image/jpeg");
String text = defaultKaptcha.createText();
System.out.println("验证码:"+text);
request.getSession().setAttribute("captcha",text);
BufferedImage image = defaultKaptcha.createImage(text);
ServletOutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
ImageIO.write(image,"jpg",outputStream);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2. 修改service逻辑
String captcha = (String) request.getSession().getAttribute("captcha");
if(StringUtils.isEmpty(captcha)||!captcha.equalsIgnoreCase(userLogin.getCode())){
return Result.error().message("验证码错误!!!");
}
3. 使用redis改造,将验证码存储到redis中
为了节省篇幅,将redis的改造放在下面实现权限管理系统的文章中 |
六、实现权限管理系统
因为篇幅限制,我将其放在这篇文章中https://blog.csdn.net/grd_java/article/details/121932440 |
七、解决高版本循环依赖和springboot2.6.x与swagger不兼容问题
实现了权限管理系统后,出现高版本循环依赖的问题,如下 |
- 当spring boot版本为2.6.X以上时,出现问题
- SecurityConfig配置类中,注入了private DdUserService ddUserService;
- DdUserServiceImpl又注入了UserDetailsService
- SecurityConfig注释private DdUserService ddUserService;,然后使用DdUserMapper
循环依赖解决,但是又出现了最近经常出现的问题,springboot2.6.x与swagger2不兼容的问题,这个和咱代码就没关系了,解决方案如下,修改配置,或者升级swagger或者降低springboot版本到2.5.x |
- 解决循环依赖,新问题如下,表面上是swagger2找不到东西了,其实是ant_path_matcher参数的问题
- 配置yaml参数
spring:
main:
allow-circular-references: true
mvc:
pathmatch:
matching-strategy: ant_path_matcher
- 问题解决,项目跑起来了,但是此时swagger不能用了,显示跨域问题
- 修改跨域配置
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}