Spring Session + REST +自定义身份验证过滤器(从JSON读取凭据而不是查询参数)

时间:2015-10-20 10:19:47

标签: spring rest spring-security spring-boot spring-session

我尝试将我的其他服务身份验证从基本身份验证转换为基于表单的身份验证,如果我在url中将身份验证详细信息作为查询参数发送,则以下代码可以正常工作(请注意我已注释掉自定义身份验证过滤器)像这样http://localhost:8080/login?username=dfdf&&password=sdsdd

但是,我并不热衷于将凭据作为查询参数发送,而是希望将其作为json发送,因此我创建了自定义身份验证过滤器。当我添加自定义身份验证筛选器时,我的spring会话停止工作。我在响应标题中找不到x-auth-token字段。任何建议如何一起启用spring会话和自定义身份验证/或者可能更容易处理json输入的凭据。

@Configuration
@EnableWebSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ObjectMapper objectMapper;

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
        builder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/", "/register", "/index.html").permitAll().and().authorizeRequests()
                .anyRequest().authenticated().and().requestCache().requestCache(new NullRequestCache()).and()
                .formLogin().failureHandler(getRESTAuthenticationFailureHandler())
                .successHandler(getRESTAuthenticationSuccessHandler()).usernameParameter("username")
                .passwordParameter("password").and().exceptionHandling()
                .authenticationEntryPoint(getRESTAuthenticationEntryPoint()).and()
                //.addFilter(getAuthenticationFilter())
                .csrf().disable();
    }

    @Bean
    public HttpSessionStrategy httpSessionStrategy() {
        return new HeaderHttpSessionStrategy();
    }

    @Bean
    public RESTAuthenticationEntryPoint getRESTAuthenticationEntryPoint() {
        return new RESTAuthenticationEntryPoint();
    }

    @Bean
    public RESTAuthenticationSuccessHandler getRESTAuthenticationSuccessHandler() {
        return new RESTAuthenticationSuccessHandler();
    }

    @Bean
    public RESTAuthenticationFailureHandler getRESTAuthenticationFailureHandler() {
        return new RESTAuthenticationFailureHandler();
    }

    @Bean
    public AuthenticationFilter getAuthenticationFilter() {
        AuthenticationFilter filter = new AuthenticationFilter();
        try {
            filter.setAuthenticationManager(this.authenticationManager());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filter;
    }

    public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {

        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException authException) throws IOException, ServletException {

            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
        }
    }

    public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication) throws IOException, ServletException {
            clearAuthenticationAttributes(request);

        }
    }

    public class RESTAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException exception) throws IOException, ServletException {

            super.onAuthenticationFailure(request, response, exception);
        }
    }

    public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

        private final Logger LOG = LoggerFactory.getLogger(AuthenticationFilter.class);

        private boolean postOnly = true;

        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
                throws AuthenticationException {
            if (postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
            }

            String username = null;
            String password = null;
            UserDetails userDetails = null;
            if ("application/json".equals(request.getHeader("Content-Type"))) {
                userDetails = getJson(request);
                if (userDetails != null) {
                    username = userDetails.getUsername();
                }
            } else {
                username = obtainUsername(request);
            }

            if ("application/json".equals(request.getHeader("Content-Type"))) {

                if (userDetails != null) {
                    password = userDetails.getPassword();
                }

            } else {
                password = obtainPassword(request);
            }

            if (username == null) {
                username = "";
            }

            if (password == null) {
                password = "";
            }

            username = username.trim();

            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,
                    password);

            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);

            return this.getAuthenticationManager().authenticate(authRequest);
        }

        private UserDetails getJson(HttpServletRequest request) {

            try {
                final List<String> data = IOUtils.readLines(request.getReader());
                final String jsonData = data.stream().collect(Collectors.joining());
                LOG.info(jsonData);
                UserDetails userDetails = objectMapper.readValue(jsonData, UserDetails.class);
                return userDetails;
            } catch (IOException e) {
                LOG.error("Failed to read data {}", e.getMessage(), e);
                return null;
            }

        }

    }

}

1 个答案:

答案 0 :(得分:1)

正如我所建议的,我已经创建了自定义过滤器,它将json对象转换为请求参数并添加到HttpServletRequest,但是有没有内置的Spring安全自定义过滤器来执行相同的工作?

@Configuration
@EnableWebSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ObjectMapper objectMapper;

    @Bean
    public FilterRegistrationBean contextFilterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        CstFilter contextFilter = new CstFilter();
        registrationBean.addUrlPatterns("/login");
        registrationBean.setFilter(contextFilter);
        registrationBean.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
        return registrationBean;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {

        builder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .csrf()
                .disable()
            .exceptionHandling()
                .authenticationEntryPoint(getRESTAuthenticationEntryPoint())
            .and()
                .formLogin()
                    .permitAll()
                    .loginProcessingUrl("/login")
                    .failureHandler(getRESTAuthenticationFailureHandler())
                    .successHandler(getRESTAuthenticationSuccessHandler())
                    .usernameParameter("username")
                    .passwordParameter("password")
            .and()
                .logout()
                    .permitAll()
                    .logoutSuccessHandler(getRESTLogoutSuccessHandler())
            .and()
                .authorizeRequests()
                    .antMatchers("/", "/index.html")
                    .permitAll()
            .and()
                .authorizeRequests()
                        .anyRequest()
                            .authenticated()
            .and()
                .requestCache()
                    .requestCache(new NullRequestCache());
    }

    @Bean
    public HttpSessionStrategy httpSessionStrategy() {
        return new HeaderHttpSessionStrategy();
    }

    @Bean
    public RESTAuthenticationEntryPoint getRESTAuthenticationEntryPoint() {
        return new RESTAuthenticationEntryPoint();
    }

    @Bean
    public RESTAuthenticationSuccessHandler getRESTAuthenticationSuccessHandler() {
        return new RESTAuthenticationSuccessHandler();
    }

    @Bean
    public RESTAuthenticationFailureHandler getRESTAuthenticationFailureHandler() {
        return new RESTAuthenticationFailureHandler();
    }

    @Bean
    public RESTLogoutSuccessHandler getRESTLogoutSuccessHandler() {
        return new RESTLogoutSuccessHandler();
    }

    public class JsonConvertFilter extends HttpServletRequestWrapper {

        private final Logger LOG = LoggerFactory.getLogger(JsonConvertFilter.class);

        private UserDetails userDetails;

        public JsonConvertFilter(HttpServletRequest request) {
            super((HttpServletRequest)request);
            userDetails = getJson();
        }

        public String getParameter(String key){


            if(userDetails!=null){
                if("username".equals(key)){
                    return  userDetails.getUsername();
                }
                if("password".equals(key)){
                    return userDetails.getPassword();
                }
            }
            System.out.println("Called wrapper");
            return super.getParameter(key);
        }

        private UserDetails getJson() {

            try {
                final List<String> data = IOUtils.readLines(super.getReader());
                final String jsonData = data.stream().collect(Collectors.joining());
                LOG.info(jsonData);
                UserDetails userDetails = objectMapper.readValue(jsonData, UserDetails.class);
                return userDetails;
            } catch (IOException e) {
                LOG.warn("Failed to read data {}", e.getMessage(), e);
                return null;
            }

        }

    }

    public class CstFilter implements Filter{


        @Override
        public void destroy() {

        }

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

            chain.doFilter(new JsonConvertFilter((HttpServletRequest)request), response);
        }

        @Override
        public void init(FilterConfig arg0) throws ServletException {

        }

    }

    public class RESTLogoutSuccessHandler implements LogoutSuccessHandler{

        @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication) throws IOException, ServletException {

            String uri = request.getRequestURI();
            if ("logout".equals(uri)) {
                response.sendError(HttpServletResponse.SC_OK, "Succesfully Logged out");
            }

        }

    }

    public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {

        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException authException) throws IOException, ServletException {

            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().print("Unauthorizated....");

        }
    }

    public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication) throws IOException, ServletException {
            clearAuthenticationAttributes(request);

        }
    }

    public class RESTAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException exception) throws IOException, ServletException {
            String uri = request.getRequestURI();
            if ("logout".equals(uri)) {
                response.sendError(HttpServletResponse.SC_OK, "Succesfully Logged out");
            } else {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
                        "Authentication Failed: " + exception.getMessage());
            }
        }
    }

}

任何人都希望使用jquery ajax进行测试。

/* Alerts the results */
        $.ajax({
            url : "login",
            type : "POST",
            async : false,
            contentType: "application/json",
            data : "{ \"username\":\""+username+"\", \"password\":\"" + password + "\"}",
            success : function(data, status, request) {
                alert("Success");
                authtokenKey = request.getResponseHeader('x-auth-token');
            },
            error : function(xhr, status, error) {
                alert(error);
            }
        });