[英]How to pass the authorization token in all request header - Spring boot java
[英]How to get bearer token from header of a request in java spring boot?
嗨,试图实现的是获取从 java spring 引导 RESTApi controllers 的前端提交的不记名令牌并使用另一个客户端请求另一个请求? 这就是我所做的
上图是我如何从 postman 提出请求,这是我的 controller 代码:
@Operation(summary = "Save new")
@PostMapping("/store")
public ResponseEntity<ResponseRequest<TransDeliveryPlanning>> saveNewTransDeliveryPlanning(
@Valid @RequestBody InputRequest<TransDeliveryPlanningDto> request) {
TransDeliveryPlanning newTransDeliveryPlanning = transDeliveryPlanningService.save(request);
ResponseRequest<TransDeliveryPlanning> response = new ResponseRequest<TransDeliveryPlanning>();
if (newTransDeliveryPlanning != null) {
response.setMessage(PESAN_SIMPAN_BERHASIL);
response.setData(newTransDeliveryPlanning);
} else {
response.setMessage(PESAN_SIMPAN_GAGAL);
}
return ResponseEntity.ok(response);
}
这是我的服务的样子:
public TransDeliveryPlanning save(InputRequest<TransDeliveryPlanningDto> request) {
Future<List<PartnerDto>> initPartners = execs.submit(getDataFromAccount(transDeliveryPlanningDtSoDtoPartnerIdsSets));
}
public Callable<List<PartnerDto>> getDataFromAccount(Set<Long> ids) {
String tokenString = "i should get the token from postman, how do i get it to here?";
List<PartnerDto> partnerDtoResponse = accountFeignClient.getData("Bearer " + tokenString, ids);
return () -> partnerDtoResponse;
}
如您所见,在“tokenString”中,我放了一个我质疑的字符串,我如何从 postman 到那里?
尽管建议的答案有效,但每次将令牌传递给FeignClient
调用仍然不是最好的方法。 我建议为假装请求创建一个拦截器,在那里您可以从RequestContextHolder
中提取令牌并将其添加到直接请求 header 中。 像这样:
@Component
public class FeignClientInterceptor implements RequestInterceptor {
private static final String AUTHORIZATION_HEADER = "Authorization";
public static String getBearerTokenHeader() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("Authorization");
}
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header(AUTHORIZATION_HEADER, getBearerTokenHeader());
}
}
这样你就有一个干净的解决方案来解决你的问题
您在这里有几个选择。
例如,您可以使用请求范围的 bean ,并按照您的建议,使用一个MVC 拦截器。
基本上,您需要为令牌值定义一个包装器:
public class BearerTokenWrapper {
private String token;
// setters and getters
}
然后,提供一个 MVC HandlerInterceptor
的实现:
public class BearerTokenInterceptor extends HandlerInterceptorAdapter {
private BearerTokenWrapper tokenWrapper;
public BearerTokenInterceptor(BearerTokenWrapper tokenWrapper) {
this.tokenWrapper = tokenWrapper;
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
final String authorizationHeaderValue = request.getHeader("Authorization");
if (authorizationHeaderValue != null && authorizationHeaderValue.startsWith("Bearer")) {
String token = authorizationHeaderValue.substring(7, authorizationHeaderValue.length());
tokenWrapper.setToken(token);
}
return true;
}
}
这个拦截器应该在你的 MVC 配置中注册。 例如:
@EnableWebMvc
@Configuration
public class WebConfiguration extends WebConfigurer { /* or WebMvcConfigurerAdapter for Spring 4 */
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(bearerTokenInterceptor());
}
@Bean
public BearerTokenInterceptor bearerTokenInterceptor() {
return new BearerTokenInterceptor(bearerTokenWrapper());
}
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public BearerTokenWrapper bearerTokenWrapper() {
return new BearerTokenWrapper();
}
}
通过此设置,您可以在Service
中使用 bean 自动装配相应的 bean:
@Autowired
private BearerTokenWrapper tokenWrapper;
//...
public TransDeliveryPlanning save(InputRequest<TransDeliveryPlanningDto> request) {
Future<List<PartnerDto>> initPartners = execs.submit(getDataFromAccount(transDeliveryPlanningDtSoDtoPartnerIdsSets));
}
public Callable<List<PartnerDto>> getDataFromAccount(Set<Long> ids) {
String tokenString = tokenWrapper.getToken();
List<PartnerDto> partnerDtoResponse = accountFeignClient.getData("Bearer " + tokenString, ids);
return () -> partnerDtoResponse;
}
此处在堆栈溢出中提供了类似的解决方案。 例如,参见这个相关的问题。
除了这种基于 Spring 的方法之外,您还可以尝试与其他 stackoverflow question中公开的解决方案类似的方法。
老实说,我从未测试过它,但似乎您可以在 Feign 客户端定义中提供请求 header 值,在您的情况下类似于:
@FeignClient(name="AccountFeignClient")
public interface AccountFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/data")
List<PartnerDto> getData(@RequestHeader("Authorization") String token, Set<Long> ids);
}
当然,也可以是其他Controller
可以扩展的普通Controller
。 这个Controller
将提供从Authorization
header 和 HTTP 请求获取不记名令牌所需的逻辑,但我认为上述任何解决方案都更好。
我有一个类似的案例。 我正在拦截来自一个微服务的请求,获取令牌并将其设置为我的新 ApiClient 并使用此 ApiClient 从另一个微服务调用端点。 但我真的不知道是否有可能预先配置 feign 客户端。 您可以做的一件事是创建 DefaultApiFilter,拦截请求,将令牌保存在数据库中(或将其设置为一些 static 变量,一些 singleton ZA2F2ED4F8EBC2CBB4C21A29DC40AB61Z 当尝试使用类似的东西或调用您的服务方法时)假装客户端:
package com.north.config;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
public class DefaultApiFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
String auth = req.getHeader("Authorization");
//TODO if you want you can persist your token here and use it on other place
//TODO This may be used for verification if it comes from the right endpoint and if you should save the token
final String requestURI = ((RequestFacade) servletRequest).getRequestURI();
filterChain.doFilter(servletRequest, servletResponse);
}
}
此doFilter
方法将始终在调用任何端点之前执行,稍后将调用端点。
之后调用accountFeignClient.getData("Bearer " + tokenString, ids);
您可以从您的数据库(或从您保存它的任何其他地方)获取它并在此处设置。
从 header 获取不记名令牌的一种简单方法是将@RequestHeader与 header 名称一起使用。
请参阅下面的代码示例
@PostMapping("/some-endpoint")
public ResponseEntity<String> someClassNmae(@RequestHeader("Authorization") String bearerToken) {
System.out.println(bearerToken); // print out bearer token
// some more code
}
我得到了答案,但我认为我仍然会等待更好的选择,因为我的答案是我必须在每个 controller 添加 @RequestHeader 以获取我的令牌的值并使用String token = headers.getFirst(HttpHeaders.AUTHORIZATION);
,这是我完整的 controller:
@Operation(summary = "Save new")
@PostMapping("/store")
public ResponseEntity<ResponseRequest<TransDeliveryPlanning>> saveNewTransDeliveryPlanning(@RequestHeader HttpHeaders headers,
@Valid @RequestBody InputRequest<TransDeliveryPlanningDto> request) {
String token = headers.getFirst(HttpHeaders.AUTHORIZATION);
TransDeliveryPlanning newTransDeliveryPlanning = transDeliveryPlanningService.save(token, request);
ResponseRequest<TransDeliveryPlanning> response = new ResponseRequest<TransDeliveryPlanning>();
if (newTransDeliveryPlanning != null) {
response.setMessage(PESAN_SIMPAN_BERHASIL);
response.setData(newTransDeliveryPlanning);
} else {
response.setMessage(PESAN_SIMPAN_GAGAL);
}
return ResponseEntity.ok(response);
}
我在某处读到有一种叫做Interceptor
的东西,所以我们不必在我认为的每个 controller 中输入@RequestHeader,但我不知道该解决方案是否或如何正确使用它。 如果有人可以用更好的方法做到这一点,我会接受你的回答
我认为@stacker 下面的答案是正确的,但我认为在某种程度上是不完整的,并且缺少“如何在 Feign 中使用它”。
为了示例,我将提供一个真实的用例,您可以在其中拦截调用者的User-Agent
到您的服务并在Feign
调用中转发它
假设您使用基于注释的Feign
客户端,您可以在所有 Feign 客户端调用中使用拦截器,而无需任何额外代码
@Configuration
@EnableFeignClients(
defaultConfiguration = DefaultFeignConfiguration.class
)
public class FeignConfig
{
}
@Configuration
@Import(FeignClientsConfiguration.class)
public class DefaultFeignConfiguration
{
@Bean
public RequestInterceptor userAgentHeaderInterceptor() {
return UserAgentHeaderInterceptor();
}
}
这是用户代理拦截器 class
public class UserAgentHeaderInterceptor extends BaseHeaderInterceptor
{
private static final String USER_AGENT = "User-Agent";
public UserAgentHeaderInterceptor()
{
super(USER_AGENT);
}
}
public class BaseHeaderInterceptor implements RequestInterceptor
{
private final String[] headerNames;
public BaseHeaderInterceptor(String... headerNames)
{
this.headerNames = headerNames;
}
@Override
public void apply(RequestTemplate template)
{
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null)
{
HttpServletRequest httpServletRequest = attributes.getRequest();
for (String headerName : headerNames)
{
String headerValue = httpServletRequest.getHeader(headerName);
if (headerValue != null && !headerValue.isEmpty())
{
template.header(headerName, headerValue);
}
}
}
}
}
在您的情况下,您只需要使用此基础 class 并以与UserAgentHeaderInterceptor
相同的方式创建自己的拦截器
使用这个注解获取前端返回的header信息: @RequestHeader("Authorization") String token
例子:
@GetMapping("/hello")
public void hello(@RequestHeader("Authorization") String token){
您可以在实用程序 class 中创建这个简单的 static 方法,并直接重用此方法。
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public class BearerTokenUtil {
public static String getBearerTokenHeader() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("Authorization");
}
}
您的服务将如下所示
public TransDeliveryPlanning save(InputRequest<TransDeliveryPlanningDto> request) {
Future<List<PartnerDto>> initPartners = execs.submit(getDataFromAccount(transDeliveryPlanningDtSoDtoPartnerIdsSets));
}
public Callable<List<PartnerDto>> getDataFromAccount(Set<Long> ids) {
List<PartnerDto> partnerDtoResponse = accountFeignClient.getData(BearerTokenUtil.getBearerTokenHeader(), ids);
return () -> partnerDtoResponse;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.