Spring Security中如何使用Cookie进行基本身份验证?

时间:2019-02-09 18:52:36

标签: spring spring-boot cookies spring-security

我正在使用Basic-Auth保护我的REST api。根据用户传递的正确凭据,控制器负责发送httpOnlysecure cookie作为响应。

@GetMapping
@ResponseStatus(value=HttpStatus.OK)
public void loginUser( final HttpServletRequest request ,final HttpServletResponse response) throws UnsupportedEncodingException {

    setAuthCookieToResonse(request,response);

}

private void setAuthCookieToResonse(final HttpServletRequest request ,final HttpServletResponse response) throws UnsupportedEncodingException {
    String cookieKey = "auth";
    String cookieValue = request.getHeader("Authorization");

    if (cookieValue != null) {
        Cookie cookie = new Cookie(cookieKey, cookieValue);
        cookie.setHttpOnly(true);
        cookie.setSecure(true);

        response.addCookie(cookie);
    }
}

因此,现在每个请求都由浏览器发送一个cookie,其中将包含Basic-Auth详细信息。但是问题是,Spring Security如何从Cookie中提取这些凭据?

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {// @formatter:off 
        httpSecurity
        .cors()
        .and().authorizeRequests()
        .antMatchers("/signup/**").permitAll()
        .anyRequest().authenticated()
        .and().httpBasic()
        .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and().csrf().disable()
        ;
     }

我的猜测是:
BasicAuthenticationFilter.class之前添加过滤器并从cookie中提取凭证,然后将这些凭证添加到HttpServletRequest的{​​{1}}标头中,该标头将传递给spring-security层。但是问题是Authorizaton没有添加标题的API。

实现此目标的正确方法是什么?

1 个答案:

答案 0 :(得分:0)

在遵循this blog之后,我使此工作正常进行。但是我很想听听其他解决方案,尤其是使用一些弹簧配置本身。 Spring是一个非常成熟的框架,它必须(应该)有一些东西可以解决这个普遍的问题。

由于storage.select = function ( items, db, where, sign, whereNew ) { let that = this; // preset sql query let query = `select ${items} from ${db}`; if ( where && sign && whereNew ) { query += ` where ${where} ${sign} ${whereNew}` }; return new Promise ( function ( resolve, reject ) { storage.each( query, [], function ( err, row ) { if ( err ) { reject( err )} else resolve( row ); }) }) }; const result = storage.select( '*', 'users', 'id', '=', 1 ) .then( row => { l( row ) }) .catch( err => { l( err ) }); 没有任何添加新标头的方法,因此我需要创建一个自定义类,该类可以向请求添加新标头,这可以由HttpServletRequest实现。这是实现。

HttpServletRequestWrapper

在Spring安全之前检查cookie的过滤器:

public final class MutableHttpServletRequest extends HttpServletRequestWrapper {
    // holds custom header and value mapping
    private final Map<String, String> customHeaders;

    public MutableHttpServletRequest(HttpServletRequest request) {
        super(request);
        this.customHeaders = new HashMap<String, String>();
    }

    public void putHeader(String name, String value) {
        this.customHeaders.put(name, value);
    }

    public String getHeader(String name) {
        // check the custom headers first
        String headerValue = customHeaders.get(name);

        if (headerValue != null) {
            return headerValue;
        }
        // else return from into the original wrapped object
        return ((HttpServletRequest) getRequest()).getHeader(name);
    }

    public Enumeration<String> getHeaderNames() {
        // create a set of the custom header names
        Set<String> set = new HashSet<String>(customHeaders.keySet());

        // now add the headers from the wrapped request object
        Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
        while (e.hasMoreElements()) {
            // add the names of the request headers into the list
            String n = e.nextElement();
            set.add(n);
        }

        // create an enumeration from the set and return
        return Collections.enumeration(set);
    }
}

最后是安全配置:

public class CheckAuthCookieFilter implements Filter {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(httpServletRequest);

        Cookie[] cookies = httpServletRequest.getCookies();

        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                logger.debug(cookie.getName() + " : " + cookie.getValue());
                if (cookie.getName().equalsIgnoreCase("auth")) {
                    mutableRequest.putHeader("Authorization", URLDecoder.decode(cookie.getValue(), "utf-8"));
                }
            }
        }

        chain.doFilter(mutableRequest, response);

    }

}

我的自定义过滤器将在Spring的@Override protected void configure(HttpSecurity httpSecurity) throws Exception {// @formatter:off httpSecurity .cors() .and().authorizeRequests() .antMatchers("/signup/**").permitAll() .anyRequest().authenticated() .and().httpBasic() .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and().csrf().disable() ; httpSecurity.addFilterBefore(new CheckAuthCookieFilter(), BasicAuthenticationFilter.class); } 之前运行。如果存在名称为BasicAuthenticationFilter的cookie(应用程序在成功登录时创建的),则该cookie会保存基本的身份验证凭据。从中提取凭证,并将其添加到auth的标头中。然后request将运行并寻找BasicAuthenticationFilter并继续其正常流程。