【问题标题】:Spring 4 API Request AuthenticationSpring 4 API 请求认证
【发布时间】:2014-01-29 08:22:31
【问题描述】:

我正在使用 Spring 4 创建 API,我需要对 API 请求进行身份验证。

目前,我创建了一个 HandlerInterceptorAdapter 来挑选与身份验证相关的标头并对这些值执行一些验证。

如果一切正常,我将 SecurityContext 设置为 Authentication 的自定义实现,然后在 postHandle 中将身份验证设置为 null。

一切都很好,除了我在 Tomcat7 中不断收到关于应用程序关闭时未删除 ThreadLocal 变量的警告。

SEVERE: The web application [] created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@6c3e4fdb]) and a value of type [org.springframework.security.core.context.SecurityContextImpl] (value [org.springframework.security.core.context.SecurityContextImpl@ffffffff: Null authentication]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.

我知道我可能做错了,如果是这样,我会喜欢一些方向。 :D

这是我的拦截器:

/**
 * Intercepts Requests to set the Authentication in the SecurityContext.
 * Sets the response to 401 - Unauthorized, if the header is missing
 */
@Component
public class AuthenticationHandlerInterceptor extends HandlerInterceptorAdapter {

    private HandlerMediator mediator;

    Logger log = LoggerFactory.getLogger(AuthenticationHandlerInterceptor.class);

    @Autowired
    public AuthenticationHandlerInterceptor(HandlerMediator mediator) {
        this.mediator = mediator;
    }


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String username = request.getHeader("authentication-username");
        String token = request.getHeader("authentication-token");

        // if the remote host is local, then override the authentication
        if (request.getRemoteHost().equals("0:0:0:0:0:0:0:1") || request.getRemoteHost().equals("127.0.0.1")) {
            log.info("On localhost, overriding authentication with localhost");
            username = "TEST";
            token = "localhost:localhost";
        }

        if (username == null || username.trim().length() == 0) {
            failAuthentication(response, "Missing Authentication Username Header");
            return false;
        }

        if (token == null || token.trim().length() == 0 || !token.contains(":")) {
            failAuthentication(response, "Missing Authentication Token Header");
            return false;
        }

        String[] keys = token.split(":");
        String appName = keys[0];
        String apikey = keys[1];

        if (!appName.equals("localhost")) {
            // we are not under localhost so we have to authenticate the application calling us
            if (mediator.executeCommand(new AuthenticateApplicationCommand(appName, apikey)) == false) {
                failAuthentication(response, "Application Token failed authentication");
                return false;
            }
        }

        SecurityContextHolder.getContext().setAuthentication(new ApiAuthentication(username, appName));
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        SecurityContextHolder.getContext().setAuthentication(null);
    }

    private void failAuthentication(HttpServletResponse response, String message) throws Exception {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("text/html;charset=UTF-8");

        ServletOutputStream out = response.getOutputStream();

        out.println(message);
        out.close();
    }
}

如何消除这些警告?

谢谢, 乔

【问题讨论】:

    标签: java spring authentication spring-mvc


    【解决方案1】:

    为了根据 HTTP 标头的内容进行身份验证,框架预见了一个定义的自定义身份验证过滤器,通过类似于此的配置插入到 spring 安全链中(另请参见answer):

    <security:http>
            ...
    
        <security:custom-filter ref="customAuthenticationFilter" after="SECURITY_CONTEXT_FILTER" /> 
    
    </security:http>    
    

    在这个过滤器中可以添加类似的代码(另见BasicAuthenticationFilter):

    try {
           ....
    
           SecurityContextHolder.getContext().setAuthentication(authResult);
    
    
     } catch (AuthenticationException failed) {
            SecurityContextHolder.clearContext();
     }
    

    查看ThreadLocalSecurityContextHolderStrategy 类,SecurityContextImpl 实例正在那里存储在 ThreadLocal 中并被清除。

    您可以尝试在 AuthenticationHandlerInterceptor 中调用 clearContext(),但最好使用框架预见的钩子,因为这应该会减少与您报告的类似的意外。

    【讨论】:

      猜你喜欢
      • 2017-05-31
      • 1970-01-01
      • 1970-01-01
      • 2012-03-13
      • 2020-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-03
      相关资源
      最近更新 更多