当前位置: 首页 > news >正文

SpringSecurity

SpringSecurity

一:快速入门:

创建好一个springboot-maven项目,写好启动类,并且编写简单的controller,加上以下依赖:
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
此时,我们会发现访问之前的简单页面,会出现:

image-20240522223032043

并且我们在控制台可以发现:

在这里插入图片描述

此时,我们输入user(账号)和以上密码,可以看到:

在这里插入图片描述

即登陆成功。

二:认证:

2.1.登录流程:

在这里插入图片描述

2.2.SpringSecurity流程分析:

在这里插入图片描述

在这里插入图片描述

具体如何debug如下:

在这里插入图片描述

以Debug模式运行,使它运行完run命令,即创建好bean。

在这里插入图片描述

搜索并且查看即可。

2.3.认证流程:

在这里插入图片描述
在这里插入图片描述

注意,这里的有些需要被替换,比如userdetailsSevice从内存中查询需要我们改成从数据库中查询。其次,在有的时候,我们需要的是给前端响应token,所以第一个实现类也需要我们改变。

在这里插入图片描述

在这里插入图片描述

我们可以把信息存储在redis中,以前我们是查询数据库,现在这样处理之后,查询效率更快。

在这里插入图片描述

2.4.替换UserDetails:

具体代码如下:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Resourceprivate SysUserService sysUserService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(SysUser::getUserName,username);SysUser user = sysUserService.getOne(lambdaQueryWrapper);if(Objects.isNull(user)){throw new RuntimeException("用户不存在或者密码错误。");}return new LoginUser(user);}
}
注意,查询完数据库之后,会返回一个UserDetails,所以我们还要编写下面的代码:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements UserDetails {private SysUser sysUser;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getPassword() {return sysUser.getPassword();}@Overridepublic String getUsername() {return sysUser.getUserName();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
注意,现在密码的解析有默认的处理器,所以如果直接在数据库中插入数据,记得加上{noop}前缀。为什么呢:
在上面的流程图中,它会通过PasswordEncoder来进行密码比对:

在这里插入图片描述

@Configuration
public class SecurityConfig extends WebSecurityConfiguration {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}
使用举例:
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;public class PasswordEncryptionTest {public static void main(String[] args) {// 创建密码编码器实例PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();// 原始密码String rawPassword = "password123";// 加密密码String encodedPassword = passwordEncoder.encode(rawPassword);System.out.println("加密后的密码:" + encodedPassword);// 验证密码boolean matches = passwordEncoder.matches(rawPassword, encodedPassword);System.out.println("验证密码是否匹配:" + matches);}
}
注意,此时数据库中的密码必须是存储的加盐之后的密码:
$2a$10$6.coJVZ02XeCRy0AOvHF5unJDE/54sfP9OfxqctmPUUoK3HooaxlW

2.5.编写登录代码:

注意,我们要改变SpringSecurity的一些默认操作,比如拦截登录页面。

在这里插入图片描述

具体业务代码如下:
@Service
public class LoginServiceIml implements LoginService {@Autowiredprivate AuthenticationConfiguration authenticationConfiguration;@Autowiredprivate RedisCache redisCache;@Overridepublic ResponseResult login(SysUser sysUser) throws Exception {UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysUser.getUserName(),sysUser.getPassword());AuthenticationManager authenticationManager = authenticationConfiguration.getAuthenticationManager();Authentication authenticate = authenticationManager.authenticate(authenticationToken);if((Objects.isNull(authenticate))){throw  new Exception("用户名或密码错误");}LoginUser loginUser = (LoginUser) authenticate.getPrincipal();Long id = loginUser.getSysUser().getId();String jwt = JwtUtil.createJWT(id.toString());redisCache.setCacheObject("login:"+id,loginUser);return new ResponseResult(200,"登录成功",new HashMap<String,String>().put("token",jwt));}
}
注意,这里我们的目标是取消登录页面的拦截,但是想要依然保留它的认证功能。

逻辑

  1. 创建一个 UsernamePasswordAuthenticationToken 对象,其中包含了用户提供的用户名和密码。
  2. authenticationConfiguration 中获取 AuthenticationManager 对象,并使用 authenticate() 方法进行身份验证,返回一个 Authentication 对象。
  3. 如果 authenticate 对象为空(即认证失败),则抛出异常并提示“用户名或密码错误”。
  4. 如果认证成功,则从 authenticate 对象中获取登录用户的信息。
  5. 根据用户的 ID 创建一个 JWT 令牌。
  6. 将登录用户信息存储到 Redis 缓存中,以便后续使用。
  7. 返回一个包含登录成功信息和生成的 JWT 令牌的响应对象。
注意:这里要记得放行登录页面:
@Override
protected void configure(HttpSecurity http) throws Exception {http//关闭csrf.csrf().disable()//不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 对于登录接口 允许匿名访问.antMatchers("/user/login").anonymous()// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();
}

代码详细解释

     //关闭csrf.csrf().disable()
关闭CSRF保护

CSRF(Cross-Site Request Forgery)是跨站请求伪造攻击,Spring Security默认开启CSRF保护。然而,对于基于JWT的无状态API认证,通常不需要CSRF保护,因此这里将其禁用。

//不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
无状态会话管理

设置会话创建策略为无状态(STATELESS),意味着Spring Security不会创建会话来存储用户的认证信息。每个请求都必须通过JWT令牌来进行认证和授权。这对于RESTful API非常重要,因为它们通常是无状态的。

  .and().authorizeRequests()// 对于登录接口 允许匿名访问.antMatchers("/user/login").anonymous()
请求授权配置
  • .and():连接前后的配置,使其生效。
  • .authorizeRequests():开始配置请求级别的安全性。
  • .antMatchers("/user/login").anonymous():允许匿名访问 /user/login 端点。即,未认证的用户也可以访问这个URL,用于用户登录。
    // 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();
其他请求需认证

除了显式允许匿名访问的端点(如登录端点)外,其他所有请求都需要经过认证。这意味着,所有其他请求都必须携带有效的JWT令牌。

//把token校验过滤器添加到过滤器链中//http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
添加自定义JWT校验过滤器(注释掉的部分)

这行代码添加了自定义的JWT校验过滤器到过滤器链中,位置在 UsernamePasswordAuthenticationFilter 之前。虽然这行代码目前被注释掉了,但它的作用如下:

  • http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);:将 jwtAuthenticationTokenFilter 这个自定义过滤器插入到过滤器链中,在 UsernamePasswordAuthenticationFilter 之前执行。

为什么要添加自定义JWT校验过滤器

JWT校验过滤器的作用是:

  1. 提取JWT令牌:从HTTP请求的头部提取JWT令牌。
  2. 验证JWT令牌:验证令牌的有效性,包括签名和过期时间。
  3. 设置认证信息:如果令牌有效,从中提取用户信息并在Spring Security上下文中设置认证信息,以便后续的授权检查。

2.6.编写过滤器代码:

在这里插入图片描述

我们把认证改成了基于jwt的认证模式,而在默认过滤器链中,不支持这种写法,所以我们要自己编写过滤器链。这里我们选择继承OncePerRequstFilter。

选择继承 OncePerRequestFilter 而不是传统的 Filter 在Spring Security中有几个重要的原因:

1. 确保每个请求只执行一次

OncePerRequestFilter 是一个基类,它确保其子类过滤器每个请求只执行一次。这对于处理诸如JWT验证这样的任务非常重要,因为我们希望每个请求只进行一次身份验证,而不是在请求链中的每一个环节都重复执行。

2. 简化实现

继承 OncePerRequestFilter 简化了过滤器的实现。它为开发者提供了一些有用的功能,简化了过滤器的实现过程,避免了手动处理某些常见的复杂情况。

3. 集成Spring框架

OncePerRequestFilter 是Spring框架的一部分,它与Spring的其他部分集成良好,特别是Spring Security。这使得在实现自定义过滤器时,可以更轻松地使用Spring提供的各种功能和工具。

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Autowiredprivate RedisCache redisCache;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {String token = request.getHeader("token");if (!StringUtils.hasText(token)) {filterChain.doFilter(request, response);return;}String userId;try {Claims claims = JwtUtil.parseJWT(token);userId = claims.getSubject();} catch (Exception e) {e.printStackTrace();throw new RuntimeException("token非法");}String redisKet = "login:" + userId;LoginUser cacheObject = redisCache.getCacheObject(redisKet);if(Objects.isNull(cacheObject)){throw new RuntimeException("用户未登录");}//TODO 获取权限信息封装到Authentication中//标识已经认证UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(cacheObject,null,null);SecurityContextHolder.getContext().setAuthentication(authenticationToken);filterChain.doFilter(request, response);}
}
注意,这里的未获取到token依然放行是因为这是一个过滤器链,此时的请求未认证,会被其他的过滤器拦截,所以我们直接放行。其次,我们这里用的是三个参数的构造函数,因为在三个参数的函数中,会进行认证操作。因为后续其他过滤器操作是在 SecurityContextHolder中获取信息,所以我们还要设置这一步。
在代码片段中:
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(cacheObject, null, null);
这个构造方法有三个参数:
  • principal(第一个参数):表示当前经过认证的用户信息。在这个例子中,cacheObjectUserDetails 的实现类对象,它包含了用户的身份信息。
  • credentials(第二个参数):表示用户的凭证信息(例如密码)。在这个例子中传递 null,因为用户已经被认证,凭证不再需要,这里是通过redis验证了。
  • authorities(第三个参数):表示用户的权限(角色)。在这个例子中传递 null,后续在授权部分会改进代码。

2.7.配置过滤器:

在我们定义好过滤器之后,还要将它加入我们的过滤器链,并且指明执行顺序。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Autowiredprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {http//关闭csrf.csrf().disable()//不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 对于登录接口 允许匿名访问.antMatchers("/user/login").anonymous()// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();//把token校验过滤器添加到过滤器链中http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
}
指明在UsernamePasswordAuthenticationFilter之前即可。

2.8.退出登录:

@Overridepublic ResponseResult<String> logout() {UsernamePasswordAuthenticationToken  authentication= (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();LoginUser loginUser = (LoginUser) authentication.getPrincipal();Long id = loginUser.getSysUser().getId();redisCache.deleteObject("login:"+id);return new ResponseResult<>(200,"退出成功");}
SecurityContextHolder.getContext().getAuthentication():这是Spring Security提供的API,用于获取当前线程的安全上下文。在默认实现中,SecurityContextHolder使用ThreadLocal来存储SecurityContext,这样可以确保每个线程有自己的安全上下文。

三:权限:

3.1.基本流程:

在这里插入图片描述

在前面存入的Holder代码中,有个TODO如下:
 @Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {String token = request.getHeader("token");if (!StringUtils.hasText(token)) {filterChain.doFilter(request, response);return;}String userId;try {Claims claims = JwtUtil.parseJWT(token);userId = claims.getSubject();} catch (Exception e) {e.printStackTrace();throw new RuntimeException("token非法");}String redisKet = "login:" + userId;LoginUser cacheObject = redisCache.getCacheObject(redisKet);if(Objects.isNull(cacheObject)){throw new RuntimeException("用户未登录");}//TODO 获取权限信息封装到Authentication中//标识已经认证UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(cacheObject,null,null);SecurityContextHolder.getContext().setAuthentication(authenticationToken);filterChain.doFilter(request, response);}

3.2.授权实现:

SpringSecurity为我们提供了基于注解的权限控制方案,这也是我们项目中主要采用的方式。我们可以使用注解去指定访问对应的资源所需的权限。

但是要使用它我们需要先开启相关配置springSecurity里面加。

@EnableGlobalMethodSecurity(prePostEnabled = true)
新版本可以用@EnableMethodSecurity 。
然后就可以使用对应的注解。@PreAuthorize
@RestController
public class HelloController {
@RequestMapping("/hello")
@PreAuthorize("hasAuthority('test')")
public String hello(){return "hello";}
}

3.3.封装权限:

回顾之前的流程:

在这里插入图片描述

首先需要我们将查询的权限加入UserDetails中:
@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {private SysUser sysUser;private List<String> permissions;@JSONField(serialize = false)private List<SimpleGrantedAuthority> authorities;public LoginUser(SysUser sysUser,List<String> permissions) {this.sysUser = sysUser;this.permissions = permissions;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if(authorities == null){authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());}return authorities;}@Overridepublic String getPassword() {return sysUser.getPassword();}@Overridepublic String getUsername() {return sysUser.getUserName();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
注意这里的注解:
@JSONField(serialize = false) 是一个来自于阿里巴巴的 fastjson 库的注解,用于控制 Java 对象在序列化和反序列化时的行为。具体来说,serialize = false 表示在对象转换为 JSON 字符串时,这个字段将被忽略,不会出现在 JSON 输出中。
再在过滤器中加上信息的封装:
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(cacheObject,null,cacheObject.getAuthorities());

3.4.从数据库中查询权限:

之前的业务是直接写好具体权限的,实际上在实际业务中需要我们去数据库中去查找,所以我们会用到:RBAC权限模型
RBAC权限模型(Role-Based Access Control)即:基于角色的权限控制。这是目前最常被开发者使用也是相对易用、通用权限模型。

在这里插入图片描述

如果只有这两个表,那么在实际运用中可能会比较麻烦,因为这些权限比较小,一个用户可能会涉及多个小的权限,就不是很方便了。所以我们引入下面的角色表:

在这里插入图片描述

最终就当是下面的五张表:

在这里插入图片描述

3.5.代码实现:

编写mapper以及xml文件:
@Mapper
public interface MenuMapper extends BaseMapper<Menu> {List<String>selectPermsByUserId(Long userId);
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.shelly.mapper.MenuMapper"><select id="selectPermsByUserId" resultType="java.lang.String">SELECTDISTINCT m.`perms`FROM sys_user_role urLEFT JOIN  `sys_role` r ON ur.`role_id` = r.`id`left join `sys_role_menu` rm on ur.`role_id` = rm.`role_id`left join `sys_menu` m on m.`id` = rm.`menu_id`WHERE user_id = #{userId} and r.status = 0  and m.status = 0</select>
</mapper>
返回UserDetails对象:
 @Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(SysUser::getUserName,username);SysUser user = sysUserService.getOne(lambdaQueryWrapper);if(Objects.isNull(user)){throw new RuntimeException("用户不存在或者密码错误。");}List<String> list = menuMapper.selectPermsByUserId(user.getId());return new LoginUser(user,list);}
接口权限要求:
		@RequestMapping("/hello")@PreAuthorize("hasAuthority('system:dept:list')")public String hello(){return "hello";}

四:其他

4.1.自定义异常处理:

在这里插入图片描述

4.1.1.认证异常处理:
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {ResponseResult result = new ResponseResult(HttpStatus.UNAUTHORIZED.value(), "用户名认证失败请重新登录");String json = JSON.toJSONString(result);//处理异常WebUtils.renderString(response, json);}
}
4.1.2.授权异常处理:
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {ResponseResult result = new ResponseResult(HttpStatus.FORBIDDEN.value(), "授权不足");String json = JSON.toJSONString(result);//处理异常WebUtils.renderString(response, json);}
}
4.1.3.配置处理器:
   @Autowiredprivate AuthenticationEntryPointImpl authenticationEntryPoint;@Autowiredprivate AccessDeniedHandlerImpl accessDeniedHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {http//关闭csrf.csrf().disable()//不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 对于登录接口 允许匿名访问.antMatchers("/user/login").anonymous()// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();//把token校验过滤器添加到过滤器链中http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler);
}

4.2.跨域:

浏览器出于安全考虑,使用XMLHttpRequest对象发起HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,默认情况下是被禁止的。同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。

前后端分离项目,前端和后端项目一般都不是同源的,所以会出现跨域问题。
1.先对SpringBoot配置,允许跨域请求:
@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {//设置允许跨域的路径registry.addMapping("/**")//设置允许跨域请求的域名.allowedOriginPatterns("*")//是否允许cookie.allowCredentials(true)//设置允许的请求方式.allowedMethods("GET","POST","DELETE","PUT")//设置允许的header属性.allowedHeaders("*")//跨域允许的时间.maxAge(3600);}
}

2. 开启SpringSecurity的跨域访问

http.cors();
在Security的config配置即可。

4.3.其他权限验证:

我们前面都是使用@PreAuthorize注解,然后在在其中使用的是hasAuthority方法进行校验,Springsecurity还为我们提供了其它方法例如: hasAnyAuthority,hasRole,hasAnyRole等。
hasAuthority:

方法实际是执行到了SecurityExpressionRoot的hasAuthority,只要断点调试既可知道它内部的校验原理。它内部其实是调用authentication的getAuthorities方法获取用户的权限列表。然后判断我们存入的方法参数数据在权限列表中。

hasAnyAuthority:

方法可以传入多个权限,只有用户有其中任意一个权限都可以访问对应资源。

hasRole:

要求有对应的角色才可以访问,但是它内部会把我们传入的参数拼接上 ROLE后再去比较。所以这种情况下要用用户对应的权限也要有 ROLE这个前缀才可可以。

hasAnyRole:

有任意的角色就可以访问。它内部也会把我们传入的参数拼接上 ROLE后再去比较。所以这种情况下要用用户对应的权限也要有 ROLE_这个前缀才可以。

4.4.自定义权限验证:

自定义配置类:
@Component("ex")
public class SGExpressionRoot{public boolean hasAuthority(String authority){//获取当前用户的权限Authentication authentication = SecurityContextHolder.getContext().getAuthentication();LoginUser loginUser = (LoginUser) authentication.getPrincipal();List<String> permissions = loginUser.getPermissions();//判断用户权限集合中是否存在authorityreturn permissions.contains(authority);}
}
在SPEL表达式中使用@ex相当于获取容器中bean的名字为ex的对象,然后在调用这个对象的hasAuthority方法
@RestController
public class HelloController{@RequestMapping("/hello")@PreAuthorize("@ex.hasAuthority('system:dept:list')")public String hello(){return "hello";}
}
基于配置的权限控制:
//基于配置的权限控制
.antMatchers("/hello").hasAuthority("system:dept:list")

4.5.CSRF:

在这里插入图片描述

CSRF是指跨站请求伪造 (Cross-site request forgery) ,是web常见的攻击之一。
SpringSecurity去防止CSRF攻击的方式就是通过csrf_token。后端会生成一个csrf_token,前端发起请求的时候需要携带这个csrf_token,后端会有过滤器进行校验,如果没有携带或者是伪造的就不允许访问。

我们可以发现CSRF攻击依靠的是cookie中所携带的认证信息。但是在前后端分离的项目中我们的认证信息其实是token,而token并不是存储中cookie中,并且需要前端代码去把token设置到请求头中才可以,所以CSRF攻击也就不用担心了。

4.6.认证处理器

在UsernamePasswordAuthenticationFilter进行登录认证的时候,如果登录成功了是会调用AuthenticationSuccessHandler的方法进行认证成功后的处理的。AuthenticationSuccessHandler就是登录成功处理器。

在UsernamePasswordAuthenticationFilter进行登录认证的时候,如果登录失败了是会调用AuthenticationFailureHandler的方法进行认证成功后的处理的。AuthenticationFailureHandler就是登录失败处理器。
我们也可以自己去自定义成功、失败处理器进行相应处理。

@Component
public class SGSuccessHandler implements AuthenticationSuccessHandler{@Overridepublic void onAuthenticationSuccess(HttpServletRequest request,HttpServletResponse response,Authentication authentication) throws IOException,ServletException{System.out.println("认证成功了");} 
}
@Component
public class SGFailureHandler implements AuthenticationFailureHandler{@Overridepublic void onAuthenticationFailure(HttpServletRequest request,HttpServletResponse response,Authentication authentication) throws IOException,ServletException{System.out.println("认证失败了");} 
}
@Component
public class SGLogoutSuccessHandler implements LogoutSuccessHandler{@Overridepublic void onLogoutSuccess(HttpServletRequest request,HttpServletResponse response,Authentication authentication) throws IOException,ServletException{System.out.println("注销成功了");} 
}
配置处理器:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{@Autowiredprivate AuthenticationSuccessHandler successHandler;@Autowiredprivate AuthenticationFailureHandler failureHandler;@Autowired private LogoutSuccessHandler logoutSuccessHandler;@Overrideprotected void configure(HttpSecurity http) throws Exception{http.formLogin()             //配置认证成功处理器.successHandler(successHandler)//配置认证失败处理器.failureHandler(failureHandler);     http.logout()//配置注销成功处理器.logoutSuccessHandler(logoutSuccessHandler);http.authorizeRequests().anyRequest().authenticated();      }
}
http://www.xdnf.cn/news/713647.html

相关文章:

  • 深入了解 C# 异步编程库 AsyncEx
  • Minimax-speech-hd
  • Qt DateTimeEdit(时间⽇期的微调框)
  • 【QQ音乐】sign签名| data参数加密 | AES-GCM加密 | webpack实战 (下)
  • ElasticSearch简介及常用操作指南
  • TypeScript中?和!号用法
  • Asp.Net Core 托管服务
  • Cannot find any provider supporting AES/ECB/PKCS7Padding
  • 智能外呼系统中 NLP 意图理解的工作原理与技术实现
  • 【前端】Vue3 中实现两个组件的动态切换保活
  • 制造企业生产数据分析全解析:5大类数据定义、分析方法与落地指南
  • 【Oracle】DCL语言
  • 【深度学习新浪潮】什么是混合精度分解?
  • Docker常用命令操作指南(一)
  • OPC Client第6讲(wxwidgets):Logger.h日志记录文件(单例模式);登录后的主界面
  • 【HTML/CSS面经】
  • 各国竞争的下一代液晶技术:中国铁电液晶取得重大突破突破
  • python和风api获取天气(JSON Web Token)
  • PostgreSQL如何更新和删除表数据
  • 【达梦数据库】内存使用资源评估
  • 图片压缩工具 | 发布到咸鱼并配置网盘自动发货
  • 通义灵码2.5——基于MCP实现我的12306火车票智能查询小助手
  • 66常用控件_QTableWidget的使用
  • 如何在 Odoo 18 中创建 PDF 报告
  • 【JavaScript 高级】事件循环机制详解
  • 第一个桌面应用程序的创建
  • 实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.2 R语言解题
  • 文科小白学习Linux系统之安全管理
  • QT使用说明
  • matlab天线阵列及GUI框架,可用于相控阵,圆形阵,矩形阵