Spring安全登录不起作用,没有返回JSESSIONID cookie,重定向失败

时间:2017-10-01 21:14:57

标签: java spring reactjs spring-security axios

我试图编写一个带有分离后端的应用程序(使用Spring Boot编写,Spring Security用于登录)和前端(ReactJS)。现在,我在成功登录后努力访问安全端点。

我想要实现的目标:对安全端点进行GET,例如' /书籍/所有&#39 ;.如果用户未登录,则返回401.如果在前端收到401,请对' / login'进行POST。然后我想要成功登录并能够成功获得GET到' / books / all'。

什么不起作用:最后一部分。我正在发布邮件给' / login'并获得200 GET。然后我再次打电话给' / books / all'并获得GET 401.此外,我不再收到担心我的JSESSIONID cookie。

我的问题:如何解决此问题?我相信它已连接到JSESSIONID(服务器没有发送有关用户成功登录的信息?)。

在前端我使用axios。

  axios.get('http://localhost:8080/rest/book/anna/all')
        .then(response => {
            console.log('response rebuild');
            console.log(response);
            if (response.status === 401 && response.request.responseURL === 'http://localhost:8080/login') {
                axios.post('http://localhost:8080/login', 'username=c&password=d')
                    .then(response => {
                        console.log('response 2');
                        console.log(response);
                    })
                    .catch(error => {
                        console.log('error');
                        console.log(error);
                    })
            }
        })
        .catch(error => {
            console.log('error 2');
            console.log(error);
            axios.post('http://localhost:8080/login', 'username=c&password=d')
                .then(response => {
                    console.log('response 2');
                    console.log(response);
                    axios.get('http://localhost:8080/rest/book/anna/all')
                        .then(response => {
                            console.log('response 3');
                            console.log(response);
                        })
                        .catch(error => {
                            console.log('error 3');
                            console.log(error);
                        })
                })
                .catch(error => {
                    console.log('error');
                    console.log(error);
                })
        });

请注意,我知道这段代码质量低;只是暂时检查登录后重定向是否正常。

SecurityConfig.java

package com.shareabook.security;

import com.shareabook.repository.UsersRepository;
import com.shareabook.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@EnableJpaRepositories(basePackageClasses = UsersRepository.class)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService userDetailsService;
    @Autowired
    private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;
    @Autowired
    private RESTAuthenticationSuccessHandler restAuthenticationSuccessHandler;
    @Autowired
    private RESTAuthenticationFailureHandler restAuthenticationFailureHandler;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                .and()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("**/anna/**").authenticated()
                .anyRequest().permitAll();
        http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint);
        http.formLogin().successHandler(restAuthenticationSuccessHandler);
        http.formLogin().failureHandler(restAuthenticationFailureHandler);
//                .and()
//                .formLogin().permitAll();

        http
                .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/rest/author/all");
    }

    private PasswordEncoder getPasswordEncoder() {
        return new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                return charSequence.toString();
            }

            @Override
            public boolean matches(CharSequence charSequence, String s) {
                return true;
            }
        };
    }
}

RESTAuthenticationEntryPoint.java

@Component
public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {

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

        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
    }
}

RESTAuthenticationFailureHandler.java

@Component
public class RESTAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

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

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

RESTAuthenticationSuccessfulHandler.java

@Component
public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

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

//        clearAuthenticationAttributes(request);
        HttpSession session = request.getSession();
        session.setAttribute("username", "c");
        response.setStatus(HttpServletResponse.SC_OK);
    }
}

BooksController.java

@RestController
@RequestMapping("/rest/book")
public class BookController {

    @CrossOrigin(origins = "http://localhost:8888")
    @PreAuthorize("hasAnyRole('ROLE_ADMIN')")
    @RequestMapping(value = "/anna/all", method = RequestMethod.GET)
    public List<String> securedHello() {
        List<String> word = new ArrayList<>();
        word.add("all");
        System.out.print(word);
        return word;
    }
}

2 个答案:

答案 0 :(得分:3)

您已在应用程序上启用了表单身份验证。在您将帖子发送到登录页面的那一刻,Spring会对您的请求进行身份验证,默认情况下会在用户会话中缓存authnetication。

稍后您可以向绑定到同一会话的服务器发送请求,而无需其他身份验证信息。但您需要提供有关您的请求的会话信息。通常,这是通过在您的下一个请求中提供JSESSIONID cookie来完成的。浏览器会自动为您执行此操作,但仅在页面重新加载后。如果您停留在同一页面,则最初只会为该页面加载的cookie将被发送回服务器。

对于SPA应用程序,我建议使用基于令牌的身份验证而不是表单。您将首先登录并接收令牌作为回应。接下来,您必须为每个请求提供Authorization标头,提供令牌作为身份验证信息(通常采用Bearer <token>形式)

答案 1 :(得分:0)

最新答案,但可能会对某人有所帮助。

尝试将标头"Access-Control-Allow-Credentials"的值"true"添加到服务器响应中。并且在axios调用的请求配置中,还将选项withCredentials设置为true