文章目录
一、SecurityConfig
import com.example.anyimenchuang.common.handler.AccessDeniedHandlerImpl;
import com.example.anyimenchuang.common.handler.AuthenticationEntryPointImpl;
import com.example.anyimenchuang.handler.SecurityLogoutSuccessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.annotation.Resource;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启权限注解功能
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Resource
private AccessDeniedHandlerImpl accessDeniedHandler;
@Resource
private AuthenticationEntryPointImpl authenticationEntryPoint;
@Resource
private SecurityLogoutSuccessHandler securityLogoutSuccessHandler;
//创建BCryptPasswordEncoder 注入容器 加密方式
@Bean
public PasswordEncoder passwordEncoder() {
//当你将此对象注入容器时,就会自动将密码进行bc的比对校验。
//如果输入的明文密码与数据库中的加密密码不匹配则报错。
//切数据库中必须存储为bc加密的密码
return new BCryptPasswordEncoder();
}
@Override //配置登录的路径 及需要认证的路径
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//关闭csrf
//不通过Session获取SecurityContext
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 允许匿名访问
.antMatchers("/user/add", "/user/login").anonymous()
//放行swagger
.antMatchers(
"/swagger-ui.html",
"/swagger-ui/index.html",
"/swagger-resources/**",
"/webjars/**",
"/swagger-ui/**",
"/v3/**",
"/api/**",
"/swagger-ui.html/**"
).permitAll()
// .antMatchers("/hello").permitAll() //允许登录或者未登录都可访问
// .antMatchers("/testCors").hasAuthority("system:dept:list222")
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
http.logout()
.logoutUrl("/user/logout")
.logoutSuccessHandler(securityLogoutSuccessHandler)
.deleteCookies("JSESSIONID");
//添加过滤器
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
//配置异常处理器
http.exceptionHandling()
//配置认证失败及授权失败处理器
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
//设置跨越
http.cors();
}
@Bean
@Override //需要通过AuthenticationManager的authenticate方法进行用户认证,所有需要在此将其注入容器
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
二、其他配置
1.JwtAuthenticationTokenFilter
代码如下(示例):
import com.alibaba.fastjson2.JSON;
import com.example.anyimenchuang.common.LoginUser;
import com.example.anyimenchuang.common.utils.sys.JwtUtils;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
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.Objects;
/**
* @Description : jwt认证过滤器 定义好后需要进行配置指定的位置 在SecurityConfig中进行配置
* 当调用其他接口时需要携带token,此处进行token验证,验证通过则放行
*/
@Component //token校验过滤器
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Resource
private RedisTemplate<String, String> redisTemplate;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 获取token
String token = request.getHeader("token");
if (StringUtils.isBlank(token)) {//如果为空
//放行 因为后面还有其他的过滤器会进行判断并提示
filterChain.doFilter(request, response);
return;
}
// 解析token
String userId;
try {
Claims claims = JwtUtils.parseJWT(token);
userId = claims.getSubject();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("token非法");
}
// 从redis中获取用户信息
String redisKey = "user:" + userId;
String value = redisTemplate.opsForValue().get(redisKey);
LoginUser loginUser = JSON.parseObject(value, LoginUser.class);//会根据类型自动转换
if (Objects.isNull(loginUser)) {
throw new RuntimeException("用户未登录");
}
// 存入SecurityContextHolder
// 获取权限信息封装到Authentication中 第三个为获取权限loginUser.getAuthorities()
UsernamePasswordAuthenticationToken userToken =
new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(userToken);
// 放行
filterChain.doFilter(request, response);
}
}
2.AccessDeniedHandlerImpl
代码如下(示例):
import com.alibaba.fastjson2.JSON;
import com.example.anyimenchuang.common.SysResult;
import com.example.anyimenchuang.common.utils.sys.WebUtils;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description : 授权失败处理器
*/
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) {
SysResult result = new SysResult().setCode(403).setMsg("您的权限不足");
String json = JSON.toJSONString(result);
//处理异常
WebUtils.renderString(response, json);
}
}
3.AccessDeniedHandlerImpl
代码如下(示例):
import com.alibaba.fastjson2.JSON;
import com.example.anyimenchuang.common.SysResult;
import com.example.anyimenchuang.common.utils.sys.WebUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description : 自定义认证失败异常处理器
*/
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
SysResult result = new SysResult().setCode(401).setMsg("用户认证失败请查询登录");
String json = JSON.toJSONString(result);
//处理异常
WebUtils.renderString(response, json);
}
}
4.AccessDeniedHandlerImpl
代码如下(示例):
import com.alibaba.fastjson2.JSON;
import com.example.anyimenchuang.common.JSONResult;
import com.example.anyimenchuang.common.utils.sys.JwtUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class SecurityLogoutSuccessHandler implements LogoutSuccessHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Resource
private RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
public SecurityLogoutSuccessHandler() {
}
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException {
String token = request.getHeader("token");
int userId = Integer.parseInt(JwtUtils.parseJWT(token).getSubject());
redisTemplate.delete("user:" + userId);
logger.info("退出成功");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(JSON.toJSONString(new JSONResult<>(200, "success")));
}
}