在我的控制器中,当我需要活动(登录)用户时,我正在执行以下操作来获取我的UserDetails实现:
User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());
Run Code Online (Sandbox Code Playgroud)
它工作正常,但我认为Spring可以在这样的情况下让生活更轻松.有没有办法将UserDetails自动装配到控制器或方法?
例如,类似于:
public ModelAndView someRequestHandler(Principal principal) { ... }
Run Code Online (Sandbox Code Playgroud)
但是,而不是得到UsernamePasswordAuthenticationToken,我得到了一个UserDetails?
我正在寻找一个优雅的解决方案.有任何想法吗?
我有一个 Spring Boot 微服务,用于验证 JWT(由不同服务颁发)进行身份验证。它运行良好,我可以像这样访问控制器中的 JWT 详细信息:
// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
// MyController.java
@RestController
@RequestMapping("/")
public class MyController {
@GetMapping()
public String someControllerMethod(@AuthenticationPrincipal Jwt jwt) {
int userId = Integer.parseInt(jwt.getClaim("userid"));
...
}
}
Run Code Online (Sandbox Code Playgroud)
效果很好。我可以从 JWT 中提取我需要的内容,然后使用正确的用户 ID 等继续与我的数据库对话。
然而,我发现必须使用 Jwt 类型在每个控制器中获取这些值有点乏味。有没有办法可以注入不同的类型作为@AuthenticationPrincipal?
例如,我自己的类已经从 JWT 中提取了所需的内容,并公开了类似.getUserId()返回 int 的内容?这也让我可以集中解析声明或抛出异常(如果它们不符合预期)的逻辑。
更新
经过更多谷歌洞穴探险后,似乎我有两个选择
选项1:@ControllerAdvice和@ModelAttribute
正如这个答案中所解释的。我可以做类似的事情:
import com.whatever.CustomPrincipal; // a basic "data" class with some properties, getters, setters and constructor
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
@ControllerAdvice
public class SecurityControllerAdvice { …Run Code Online (Sandbox Code Playgroud) Spring Security假设Authentication是主体。
public interface Authentication extends Principal, Serializable {}
Run Code Online (Sandbox Code Playgroud)
HttpServletRequest具有getUserPrincipal方法,该方法负责访问主体对象。
让我们考虑这种情况:
public interface RealPrincipal extends Principal {
public Integer getId();
}
Run Code Online (Sandbox Code Playgroud)
通用模块A具有Real Principal接口和实现。
模块A使用通用模块A,Servlet Api,并且不依赖于Spring Security:
模块B使用通用模块A,Servlet Api并配置Spring Security。该模块负责安全性和UserDetails的实现。
Web A使用模块A和模块B。
为了使用请求方法,我最终得到了这样的实现:
public ModelAndView someRequestHandler(Principal principal) {
User activeUser = (User) ((Authentication) principal).getPrincipal();
...
}
Run Code Online (Sandbox Code Playgroud)
这迫使我必须对模块A和其他模块依赖Spring Security。我相信适当的servlet api抽象不应依赖于spring安全性。request.getUserPrincipal应该返回真实的主体。
请解释为什么org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper返回
验证而不是Real Principal。
编辑:我已将通用模块A添加到我的方案,并更新了模块B负责安全性。
Spring MVC 控制器方法接受在调用方法之前注入的不同参数。像HttpServletRequest,HttpServletResponse,java.security.Principal等等。
@RequestMapping("/test")
public String test(HttpServletRequest req, Principal user){}
Run Code Online (Sandbox Code Playgroud)
如何声明可以在 controlelr 方法中注入的内容?
@RequestMapping("/test")
public String test(MyCustomInjectable myInjectable){}
Run Code Online (Sandbox Code Playgroud)
关于具体案例的更多信息:
我想HttpServletRequest在一些 servlet 过滤器中解析并构造一个将在控制器方法中使用的对象。更准确地说,我将解析 JWT 令牌并访问声明。