控制器之外的Jersey + Spring上下文

时间:2013-09-15 20:28:01

标签: java jersey jax-rs

我有一个需要来自请求的信息的Spring bean,但不是直接从控制器调用的(虽然它可能是 - 但我想在没有它的情况下尝试这个)

基本上,我的API通过thrift向其他服务发出请求。当它发出请求时,会有这样的服务调用:

authenticationService.authenticate(null, "username", "password");

第一个参数(null)通常是请求上下文的“占位符”实例。请求上下文包含有关发出请求的用户,原始IP等的信息。这样,我可以获得有关原始调用者的所有详细信息,而不会让我的API基础结构泄漏到后端。

但是,要做到这一点,我有一个InvocationHandler拦截对我的服务接口的代理进行的方法调用。在该代理处理程序内部,我有一个RequestContextFactory连线,用于创建RequestContext的实例。在这个工厂里面,我需要从请求中获取信息。特别是SecurityContext,因此我可以识别拨打电话的用户。

现在,我有:

@Provider
@Component
public class WebRequestContextFactory implements RequestContextFactory {
    @Context private ContainerRequest containerRequest;

    public RequestContext createRequestContext() {

    }
}

不幸的是,containerRequest始终是null

4 个答案:

答案 0 :(得分:1)

您可以使用ServletRequestAttributesrequest获取信息,ServletRequestAttributes可以从RequestContextHolder获取信息:

ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
                .currentRequestAttributes();

如果请求由Spring DispatcherServlet处理,则无需任何特殊设置。 DispatcherServlet已经暴露了所有相关的州。但是如果请求是在Spring的DispatcherServlet之外处理的,那么您需要在应用程序的web.xml文件中添加javax.servlet.ServletRequestListener

    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>

这会将请求与当前线程相关联,然后可以通过RequestContextHolder检索关联的请求属性。

答案 1 :(得分:0)

对于身份验证,最好使用容器领域,或者使用普通的servlet。

对于Authorization,您可以使用Application或rest servlet。在这种过程中,您可以从上下文注释中找到信息。这是样本:

(@Context final SecurityContext sc, @Context Request request) {
        logMe(sc);
...
    }

    private void logMe(final SecurityContext sc) {
        try {
            LOGGER.info("User=" + sc.getUserPrincipal().getName());
            LOGGER.info("User Role?=" + sc.isUserInRole("user"));
            LOGGER.info("Auth way=" + sc.getAuthenticationScheme());
        } catch (final Exception e) {
            LOGGER.debug(e);
        }
    }

或者:

(@Context final SecurityContext sc, @Context ContainerRequestContext request) {
...

答案 2 :(得分:0)

您可以创建包含您的所需信息(如IP地址,用户名等)的访问令牌。在身份验证阶段,创建自定义令牌并将此令牌放入spring安全上下文中。稍后您可以从代理类中的其他位置提取此令牌。提取您验证的令牌或您想要的任何内容后。

创建自定义对象和令牌:

public class CustomAuthentication {

  private String userId;

  private String password;

  private String ipAddress;
}


public class CustomAuthenticationToken extends AbstractAuthenticationToken {

    private CustomAuthentication customAuthentication;

    public CustomAuthenticationToken(MobiLabAuthentication authentication,
      Collection<? extends GrantedAuthority> authorities) {
      super(authorities);
      this.customAuthentication = authentication;
      setAuthenticated(true);
    }

    public CustomAuthenticationToken() {
      super(null);
      setAuthenticated(false);
    }

    @Override
    public Object getCredentials() {
     return customAuthentication.getPassword();
    }

    @Override
    public Object getPrincipal() {
      return customAuthentication.getUserId();
    }

}

将令牌存储到Spring安全上下文

List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new RestUserAuthrity("YOUR_APP_ROLE"));

//Extract IP , user and pass etc and construct CustomAuthentication instance
CustomAuthentication authentication = new CustomAuthentication(.....)

CustomAuthenticationToken authenticationToken = new CustomAuthenticationToken(
     authentication, authorities);

SecurityContextHolder.getContext().setAuthentication(authenticationToken);

验证来自Proxy bean的安全信息

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();

if (authentication instanceof CustomAuthenticationToken) {
  CustomAuthenticationToken token = (CustomAuthenticationToken) authentication;
 //now you can get your ip address from token
}

答案 3 :(得分:0)

在稍微不同的情况下,以下内容对我有用。 YMMV。

替换此:

@Context private ContainerRequest containerRequest;

与此:

@Context private javax.inject.Provider<ContainerRequest> containerRequestProvider;

然后在代码中需要ContainerRequest的位置:

ContainerRequest containerRequest = containerRequestProvider.get();

代码示例将我引向了该解决方案:https://github.com/psamsotha/jersey-okhttp-interceptor-demo/blob/b3b2da00b284e75011ea780fb37b555ea581ac96/src/main/java/com/example/stackoverflow/client/UserFactory.java