使用Java和Angular2登录Spring Security

2022-09-02 10:41:55

我在将弹簧安全性(即登录部分)集成到我的Web应用程序中遇到了一些问题。我的后端在localhost:8080上运行,前端(Ionic 2与AngularJS 2)在localhost:8100上运行。我设法登录了(至少我认为是这样),但其余请求变得不可用,我在Chrome开发人员控制台中收到以下错误:

XMLHttpRequest cannot load http://localhost:8080/company/getAll. Response for preflight is invalid (redirect)

当使用Postman进行测试时,它似乎正在工作,我登录,然后我能够在 http://localhost:8080/company/getAll 上执行POST请求,一切都按预期工作。

大概我错过了一些东西(比如令牌),但不知道是什么。我是Ionic和Spring Security的新手,所以如果这是微不足道的事情,请原谅我。我尝试在谷歌上搜索各种教程,但找不到任何东西(其中大多数都使用JSP)。

我怎样才能让它工作?我从前端发出的请求应该是什么样子的?

这是我从后端控制器登录的方法:

@RequestMapping(value = "/login", method = RequestMethod.GET)
    @CrossOrigin(origins = "*")
    public ResponseEntity<GeneralResponse> login(Model model, String error, String logout) {
        System.out.println("LOGIN"+model.toString());

        if (error != null) {
            System.out.println("1.IF");
            model.addAttribute("error", "Your username and password is invalid.");
        }

        if (logout != null) {
            System.out.println("2.IF");
            model.addAttribute("message", "You have been logged out successfully.");
        }

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new GeneralResponse(true, "FAILURE: Error"));
    }

这是我的网络安全配置:

@Configuration
@EnableWebSecurity
@ComponentScan("com.SAB.service")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/resources/**", "/registration").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/user/login")
            .permitAll()
            .and()
            .logout()
            .permitAll();
        http
            .csrf().disable();
        http.formLogin().defaultSuccessUrl("http://localhost:8100/", true);
        //.and().exceptionHandling().accessDeniedPage("/403")
        //.and().sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }
}

最后是我负责登录的前端代码:

  login(){
    var body = 'username='+this.loginForm.controls['username'].value+'&password='+this.loginForm.controls['password'].value;
    var headers = new Headers();
    headers.append('Content-Type', 'application/x-www-form-urlencoded');
    //headers.append("Access-Control-Allow-Origin", "*");

    this.http
      .post('http://localhost:8080/user/login',
        body, {
          headers: headers
        })
      .subscribe(data => {
        console.log('ok');
        console.log(data)
      }, error => {
        console.log(JSON.stringify(error.json()));
      });
  }

如果您需要任何其他详细信息,请告诉我,我会提供它们。

提前致谢!

我没有收到Access-COntroll-Origin错误,但我尝试根据评论进行更改,现在我得到“无效的CORS请求”

从邮递员登录的请求和响应标头:enter image description here

编辑:以下是春季安全日志。但是,Spring只注册一个OPTIONS请求,即使前端正在调用POST,也没有POST。

************************************************************

Request received for OPTIONS '/login':

org.apache.catalina.connector.RequestFacade@18859793

servletPath:/login
pathInfo:null
headers: 
host: localhost:8080
connection: keep-alive
access-control-request-method: POST
origin: http://localhost:8100
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36
access-control-request-headers: access-control-allow-origin
accept: */*
referer: http://localhost:8100/
accept-encoding: gzip, deflate, sdch, br
accept-language: en-US,en;q=0.8


Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  CorsFilter
  LogoutFilter
  UsernamePasswordAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]


************************************************************
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /login at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /login at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2017-05-12 19:18:28.529 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /login at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2017-05-12 19:18:28.529 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /login at position 4 of 12 in additional filter chain; firing Filter: 'CorsFilter'
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@5baaaa2b
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

更新:因此,在添加已接受的答案中建议的CORSFilter之后,我还必须在前端修改我的请求,以便在每个请求上发送带有JSESSIONID的cookie。在低音上,我不得不在我所有的要求中添加以下内容:let options = new RequestOptions({ headers: headers, withCredentials: true });


答案 1

这似乎是与 CORS 配置相关的问题。浏览器会在实际 GET 调用之前发出 OPTION 预检请求。我在这方面没有太多知识,但你可以尝试将下面的过滤器添加到弹簧后端。

public class CORSFilter extends GenericFilterBean {

    public void doFilter(ServletRequest request, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse response = (HttpServletResponse) res;
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.addHeader("Access-Control-Max-Age", "3600");
        response.addHeader("Access-Control-Allow-Headers", "X-Requested-With, X-Auth-Token");
        response.addHeader("Access-Control-Allow-Credentials", "true");

        if(req.getMethod().equalsIgnoreCase("options")){
             return;
        }
        chain.doFilter(request, response);
    }
}

并在WebSecurity配置的配置方法中添加以下行。

.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);


答案 2

解决方案非常简单。POST上的用户名和密码需要作为表单数据提交,而不是正文的一部分。我的应用程序遇到了同样的问题,经过长时间的斗争,我创建了一个HTML表单并做了一个POST,然后它就像一个魅力。

请参阅下面的实时示例。您应该能够使用任何ID注册,并尝试登录并在浏览器中首次亮相。它在服务器端使用Spring boot,在UI上使用Angular 4。

现场演示:http://shop-venkatvp.rhcloud.com/#/

登录 HTML: https://github.com/reflexdemon/shop/blob/master/src/app/login/login.component.html#L7-L31

弹簧安全配置:https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/WebSecurityConfig.java#L20-L34

登录控制器:https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/page/controller/LoginController.java

希望这是有帮助的。