总结一下写项目的时候用到的SpringSecurity中间件
注意包的结构
1.添加SpringSecurity依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2. 自定UserDetails类
当实体对象字段不满足时需要自定义UserDetails,一般都要自定义 UserDetails。
User实体类需要实现Serializable, UserDetails接口
字段按照Security的要求字段(即除了自己创建的字段还需另外增加Security需要的对应的字段)
/**
* <p>
*
* </p>
*
* @author Jeffrey
* @since 2023-10-20
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("sys_user")
public class User implements Serializable , UserDetails {
//省略了自己的字段
//另外需要添加的
private Integer isDelete;
/**
* 帐户是否过期(1 未过期,0已过期)
*/
private boolean isAccountNonExpired = true;
/**
* 帐户是否被锁定(1 未过期,0已过期)
*/
private boolean isAccountNonLocked = true;
/**
* 密码是否过期(1 未过期,0已过期)
*/
private boolean isCredentialsNonExpired = true;
/**
* 帐户是否可用(1 可用,0 删除用户)
*/
private boolean isEnabled = true;
/**
* 权限列表
*/
@TableField(exist = false)
Collection<? extends GrantedAuthority> authorities;
/**
* 查询用户权限列表
*/
@TableField(exist = false)
private List<Permission> permissionList;
}
3. 自定义UserDetailsService类
主要用于从数据库查询用户信息。
@Component
public class CustomerUserDetailsService implements UserDetailsService {
@Resource
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//调用根据用户名查询用户信息的方法
User user = userService.findUserByUserName(username);
//如果对象为空,则认证失败
if (user == null) {
throw new UsernameNotFoundException("用户名或密码错误!");
}
return user;
}
}
4. 创建登录认证成功处理器
认证成功后需要返回JSON数据,菜单权限等。
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//设置客户端的响应的内容类型
response.setContentType("application/json;charset=UTF-8");
//获取当登录用户信息
User user = (User) authentication.getPrincipal();
//消除循环引用
String result = JSON.toJSONString(user, SerializerFeature.DisableCircularReferenceDetect);
//获取输出流
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}
5. 创建登录认证失败处理器
认证失败需要返回JSON数据,给前端判断。
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
//设置客户端响应编码格式
response.setContentType("application/json;charset=UTF-8");
//获取输出流
ServletOutputStream outputStream = response.getOutputStream();
String message = null;//提示信息
int code = 500;//错误编码
//判断异常类型
if(exception instanceof AccountExpiredException){
message = "账户过期,登录失败!";
}else if(exception instanceof BadCredentialsException){
message = "用户名或密码错误,登录失败!";
}else if(exception instanceof CredentialsExpiredException){
message = "密码过期,登录失败!";
}else if(exception instanceof DisabledException){
message = "账户被禁用,登录失败!";
}else if(exception instanceof LockedException){
message = "账户被锁,登录失败!";
}else if(exception instanceof InternalAuthenticationServiceException){
message = "账户不存在,登录失败!";
}else{
message = "登录失败!";
}
//将错误信息转换成JSON
String result = JSON.toJSONString(Result.error().code(ResultCode.ERROR).message(message));
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}
6. 创建匿名用户访问无权限资源时处理器
匿名用户访问时,需要提示JSON。
@Component
public class AnonymousAuthenticationHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse
response, AuthenticationException authException) throws IOException,
ServletException {
//设置客户端的响应的内容类型
response.setContentType("application/json;charset=UTF-8");
//获取输出流
ServletOutputStream outputStream = response.getOutputStream();
//消除循环引用
String result = JSON.toJSONString(Result.error().code(ResultCode.NO_LOGIN).message("匿名用户无权限访问!")
, SerializerFeature.DisableCircularReferenceDetect);
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}
7. 创建认证过的用户访问无权限资源时的处理器
无权限访问时,需要提示JSON。
@Component
public class CustomerAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException
{
//设置客户端的响应的内容类型
response.setContentType("application/json;charset=UTF-8");
//获取输出流
ServletOutputStream outputStream = response.getOutputStream();
//消除循环引用
String result = JSON.toJSONString(Result.error().code(ResultCode.NO_AUTH).message("无权限访问,请联系管理员!")
, SerializerFeature.DisableCircularReferenceDetect);
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}
8. 配置Spring Security配置类
把上面自定义的处理器交给Spring Security。
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private CustomerUserDetailsService customerUserDetailsService;
@Resource
private LoginSuccessHandler loginSuccessHandler;
@Resource
private LoginFailureHandler loginFailureHandler;
@Resource
private AnonymousAuthenticationHandler anonymousAuthenticationHandler;
@Resource
private CustomerAccessDeniedHandler customerAccessDeniedHandler;
/**
* 注入加密处理类
*
* @return
*/
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//登录前进行过滤
http.formLogin() //配置了基于表单的身份验证
.loginProcessingUrl("/api/user/login")//登录请求url地址,自定义
// 设置登录验证成功或失败后的的跳转地址
.successHandler(loginSuccessHandler).failureHandler(loginFailureHandler)
.and().csrf().disable() //禁用了CSRF(Cross-Site Request Forgery)防护
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) //不创建和管理会话,每个请求都是独立的
.and()
.authorizeRequests() //设置需要拦截的请求
.antMatchers("/api/user/login").permitAll() //登录放行
.anyRequest().authenticated() //其他一律需要身份认证
.and()
.exceptionHandling()
.authenticationEntryPoint(anonymousAuthenticationHandler) //匿名无权限访问
.accessDeniedHandler(customerAccessDeniedHandler) //认证用户无权限访问
.and().cors();//开启跨域配置
}
/**
* 配置认证处理器
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(customerUserDetailsService).passwordEncoder(passwordEncoder());
}
}