Java Spring Security
在某些场景中需要获取当前的用户是谁?如果使用了Spring Secrity作为安全框架可以通过以下手段获取当前用户。
SecurityContext
无论是有状态的Session模式还是流行的JWT模式都可以通过SecurityContext来获取当前的用户:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();String currentPrincipalName = authentication.getName();
当然这种方式是不够严谨的,如果接口允许匿名访问很可能返回一个匿名用户,而匿名用户并不能直接通过getName获取,所以需要优化上面的逻辑为:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (!(authentication instanceof AnonymousAuthenticationToken)) {String currentUserName = authentication.getName();return currentUserName;}else{throw RuntimeException("No User")}
其实平常使用这种方式的最多,使用一个抽象的父类控制器来封装获取当前用户的方法。
Principal
java.security.Principal对象也可以获取当前的用户信息,在Spring Security中该对象表现为Authentication对象,如果在Spring MVC接口中定义Principal对象也可以获取当前用户:
@GetMapping("/currentusername")public String currentUserName(Principal principal) {return principal.getName();}
同理Authentication对象也是可以的:
@GetMapping("/currentusername")public String currentUserName(Authentication authentication) {return authentication.getName();}
AuthenticationPrincipal
很多时候自定义了用户对象UserDetails,可以通过Spring Security 4.0提供的注解@AuthenticationPrincipal来获取当前用户的自定义UserDetails对象。如果CustomUser是UserDetails的实现,那么可以:
@GetMapping("/currentusername")public String currentUserName(@AuthenticationPrincipal CustomUser customUser) {return customUser.getUsername();}
更简单点的话:
@GetMapping("/currentusername")public String currentUserName(@AuthenticationPrincipal(expression = "username") String username) {return username;}
这需要CustomUser包含一个getUsername方法。
甚至自定义一个注解也是可以的:
@Target({ElementType.PARAMETER, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@AuthenticationPrincipalpublic @interface CurrentUser {}
CurrentSecurityContext
Spring Security 5 提供了一个新的注解@CurrentSecurityContext来获取当前用户的安全上下文,可以:
@GetMapping("/currentusername")public String currentUserName(@CurrentSecurityContext(expression = "authentication")Authentication authentication) {return authentication.getName();}
当然还可以通过expression参数声明SpEL表达式来获取其它属性,例如获取Principal对象:
@GetMapping("/principal")public String getPrincipal(@CurrentSecurityContext(expression = "authentication.principal")Principal principal) {return principal.getName();}
HttpServletRequest
据说HttpServletRequest的getUserPrincipal()方法也可以。
