解决spring security的403自定义错误
package com.tc.tcmerchantsapi.config.filter;
import com.tc.tcmerchantsapi.exception.CustomException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private CustomPasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (userDetails == null || !passwordEncoder.matches(password, userDetails.getPassword())) {
throw new CustomException("登录失败请重新登录");
}
return new UsernamePasswordAuthenticationToken(username, password, userDetails.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
使用了自定义的加密方式去验证
package com.tc.tcmerchantsapi.config.filter;
import com.tc.tcmerchantsapi.util.CacheUtil;
import com.tc.tcmerchantsapi.util.RsaUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class CustomPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return RsaUtils.encrypt(rawPassword.toString(), (String) CacheUtil.getInstance().getCacheData(rawPassword.toString()));
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.equals(encodedPassword);
}
}
``package com.tc.tcmerchantsapi.config.filter;
import com.alibaba.fastjson.JSON;
import com.tc.common.core.redis.RedisCache;
import com.tc.tcmerchantsapi.exception.CustomException;
import com.tc.tcmerchantsapi.pojo.MerchantInfo;
import com.tc.tcmerchantsapi.util.RsaUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Resource
private RedisCache redisCache;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MerchantInfo user = JSON.parseObject(redisCache.get(username),MerchantInfo.class);
if (user == null) {
throw new CustomException("用户错误请重新登录");
}
return new org.springframework.security.core.userdetails.User(
user.getAccount(),
RsaUtils.decrypt(user.getPasswd(), user.getPrivateKey()),
new ArrayList<>()
);
}
}``
```bash 加token的心跳
package com.tc.tcmerchantsapi.config.filter;
import com.tc.common.core.redis.RedisCache;
import com.tc.tcmerchantsapi.config.provider.JwtTokenProvider;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
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.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@Component
public class JwtAuthenticationFilters extends OncePerRequestFilter {
@Resource
private JwtTokenProvider tokenProvider;
@Autowired(required = false)
private UserDetailsService userDetailsService;
@Resource
public RedisCache redisCache;
@SneakyThrows
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
String jwt = getJwtFromRequest(request);
if (jwt != null && tokenProvider.validateToken(jwt)) {
String username = tokenProvider.getUsernameFromJWT(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (userDetails != null) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
String user = redisCache.get(jwt);
Date expiryDate = tokenProvider.getExpiryDateFromJWT(jwt);
Date now = new Date();
long timeLeft = expiryDate.getTime() - now.getTime();
if (timeLeft < 300000L) {
String newToken = tokenProvider.generateToken(username);
response.setHeader("Authorization", "Bearer " + newToken);
redisCache.set(newToken,user,JwtTokenProvider.JWT_EXPIRATION, TimeUnit.MILLISECONDS);
redisCache.deleteObject(username);
redisCache.set(username, user,JwtTokenProvider.JWT_EXPIRATION, TimeUnit.MILLISECONDS);
}
}
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
package com.tc.tcmerchantsapi.config.handler;
import com.alibaba.fastjson.JSONObject;
import com.tc.tcmerchantsapi.exception.CustomErrorResponse;
import lombok.extern.slf4j.Slf4j;
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;
@Component
@Slf4j
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
CustomErrorResponse errorResponse = new CustomErrorResponse(HttpServletResponse.SC_FORBIDDEN,"Access Denied: 请登录");
response.getWriter().write(JSONObject.toJSONString(errorResponse));
}
}
package com.tc.tcmerchantsapi.config.provider;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtTokenProvider {
private final String JWT_SECRET = "key";
public static long JWT_EXPIRATION = 1800000L; // 30分钟
public String generateToken(String username) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + JWT_EXPIRATION);
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, JWT_SECRET)
.compact();
}
public String getUsernameFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(JWT_SECRET)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(authToken);
return true;
} catch (Exception e) {
return false;
}
}
public Date getExpiryDateFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(JWT_SECRET)
.parseClaimsJws(token)
.getBody();
return claims.getExpiration();
}
}
package com.tc.tcmerchantsapi.config;
import com.tc.tcmerchantsapi.config.filter.CustomAuthenticationProvider;
import com.tc.tcmerchantsapi.config.filter.CustomPasswordEncoder;
import com.tc.tcmerchantsapi.config.filter.CustomUserDetailsService;
import com.tc.tcmerchantsapi.config.filter.JwtAuthenticationFilters;
import com.tc.tcmerchantsapi.config.handler.CustomAuthenticationEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.annotation.Resource;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private CustomAuthenticationProvider customAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public JwtAuthenticationFilters jwtAuthenticationFilter() {
return new JwtAuthenticationFilters();
}
@Bean
@Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder()
{
return new CustomPasswordEncoder();
}
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
auth.authenticationProvider(customAuthenticationProvider);
}
@Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return new CustomAuthenticationEntryPoint();
}
@Bean
public UserDetailsService userDetailsService(){
return new CustomUserDetailsService();
}
}
package com.tc.tcmerchantsapi.exception;
import lombok.Data;
@Data
public class CustomErrorResponse {
private int code;
private String msg;
public CustomErrorResponse(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
}