登录用户信息获取
884字约3分钟
2024-08-08
获取登录用户信息
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
</dependencies>
SecurityConfig.java
/**
* @ClassName SecurityConfig
* @Desciption Security 配置
* @Author MaRui
* @Date 2023/8/11 14:31
* @Version 1.0
*/
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// authorizeHttpRequests():开启权限配置
http.authorizeHttpRequests((auth) ->{
try {
// anyRequest().authenticated():所有请求都需要认证
auth.anyRequest().authenticated()
// and():返回 HttpSecurity,继续配置
.and()
// formLogin():开启表单登录,引入表单登录过滤器(默认引入)
.formLogin()
.usernameParameter("username")
.passwordParameter("password")
// loginPage():自定义登录页面
.loginPage("/login.html")
// loginProcessingUrl():定义登录提交的接口地址
.loginProcessingUrl("/login")
// defaultSuccessUrl():登录成功后跳转地址
.defaultSuccessUrl("/index/info")
// failureUrl():登录失败后跳转地址
.failureUrl("/login.html")
// permitAll():不需要认证的请求地址
.permitAll()
.and()
// csrf().disable():csrf 防护,默认是开启。为了测试方便,暂时禁用
.csrf().disable();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return http.build();
}
}
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
用户名:<input name="username" type="text"><br>
密码:<input name="password" type="password"><br>
<button type="submit">登陆</button>
</form>
</body>
</html>
通过 SecurityContextHolder 获取
/**
* 从 SecurityContextHolder 获取当前用户登录信息
* @return
*/
@GetMapping(value = "/info")
public String getInfo() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return JSON.toJSONString(authentication);
}

过注入 Authentication 的方式获取当前用户登录信息
/**
* 通过注入 Authentication 的方式获取当前用户登录信息
* @return
*/
@GetMapping(value = "/authentication")
public String authentication(Authentication authentication) {
return JSON.toJSONString(authentication);
}

不管使用哪种方法,都是可以获得当前用户信息,但是如果你想在任何地方都能拿到登录用户信息,则只能使用SecurityContextHolder
异步线程中获取当前登录信息
很多同学在学习了通过SecurityContextHolder之后,就想尝试下,发现在正常使用中,是可以拿到用户信息的,但是在异步线程中就拿不到了,这个其实可以通过调整他的策略来实现在异步线程中获取用户信息。SecurityContextHolder 主要有三种策略:
MODE_THREADLOCAL:默认策略,使用THREADLOCAL实现,只在当前线程中保存用户信息
MODE_INHERITABLETHREADLOCAL:支持在多线程中传递用户信息,使用这种策略,当我们启动新线程时,security会将当前线程的用户信息拷贝一份到新线程中
MODE_GLOBAL:security将数据保存在一个全局变量中,也能解决多线程问题,一般很少用
如果使用默认策略,在异步线程中获取用户信息,返回为空:

调整vm配置,使用MODE_INHERITABLETHREADLOCAL策略 添加VM OPTION: -Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL



用户登录信息保存
SecurityContextHolderFilter
public class SecurityContextHolderFilter extends OncePerRequestFilter {
private final SecurityContextRepository securityContextRepository;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
SecurityContext securityContext = this.securityContextRepository.loadContext(request).get();
try {
SecurityContextHolder.setContext(securityContext);
filterChain.doFilter(request, response);
}
finally {
SecurityContextHolder.clearContext();
}
}
}
这个过滤器主要做了如下几件事:
从 securityContextRepository(SecurityContextRepository)中读取context,你可以把 securityContextRepository 当作一个数据来源,主要有以下几个实现:
HttpSessionSecurityContextRepository 默认实现
RequestAttributeSecurityContextRepository
NullSecurityContextRepository 其实什么都没做,不想用session可以使用这个
2、将context 塞入SecurityContextHolder
3、执行剩下的过滤器
4、finally 清除线程变量信息