使用Java和Angular2登录Spring Security

时间:2017-05-09 16:22:54

标签: java spring angular spring-security ionic2

我在使用Spring安全性(即登录部分)时遇到了一些问题。我的BackEnd在 localhost:8080 上运行,而FrontEnd(Ionic 2与AngularJS 2)在 localhost:8100 上运行。我设法登录(至少我是这么认为),但其他请求变得不可用,我在Chrome开发者控制台中收到以下错误:

WITH RESULTS as (
SELECT ST_Difference(public.roadnetwork.geom, public.sub_polygon.geom)
FROM public.roadnetwork, public.sub_polygon
),

 FILTERED_RESULTS as (
  SELECT RESULTS.st_difference FROM RESULTS where GeometryType(RESULTS.st_difference) <> 'GEOMETRYCOLLECTION'
),

expanded_results as (
    select (a.p_geom).geom
    from (SELECT ST_Dump(FILTERED_RESULTS.st_difference))
),

SELECT * from expanded_results;

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

据推测,我遗漏了一些东西(比如令牌)但却无法弄清楚是什么。我是Ionic和Spring Security的新手,所以请原谅我,如果这是微不足道的话。我尝试使用谷歌搜索各种教程,但无法找到任何东西(大多数都使用JSP)。

我怎么能让这个工作?我的前端请求应该如何?

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

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

这是我的WebSecurityConfig:

@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"));
    }

最后我的FrontEnd代码负责登录:

@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());
    }
}

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

提前致谢!

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

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

修改 以下是Spring Security日志。但是,即使FrontEnd正在调用POST,Spring也只注册一个OPTIONS请求而没有POST。

  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()));
      });
  }

更新 因此,在接受回答中建议的CORSFilter之后,我还必须在前端修改我的请求,以便在每个请求上发送带有JSESSIONID的cookie。我不得不将以下内容添加到我的所有请求中: ************************************************************ 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

5 个答案:

答案 0 :(得分:9)

这似乎是与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);
    }
}

并在WebSecurityConfig的configure方法中添加以下行。

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

答案 1 :(得分:2)

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

见下面的实例。您应该可以使用任何ID注册并尝试登录并在浏览器中首次亮相。它在服务器端使用Spring引导,在UI上使用Angular 4。

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

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

Spring安全配置: 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

希望这有用。

答案 2 :(得分:2)

我正在编写Angular 4和spring boot集成文章。我还没有完成文章,但源代码已完成。我不确定您为什么要尝试从两个不同的主机提供应用程序,这会通过注入cors要求并且必须管理两个安全上下文而不必要地使您的应用程序复杂化。如果这不是绝对的要求,我会从同一台服务器上提供服务。

看一下这个github项目,了解角度和弹簧启动的完整集成:https://github.com/tschiman/tutorials/tree/master/spring-cloud/spring-cloud-bootstrap/gateway。如果你不需要,你可以忽略那里的zuul东西。它对角度ui的支持没有影响。

如果你想启用csrf,你应该看看这个提交:be4b206478c136d24ea1bf8b6f93da0f3d86a6c3

如果阅读没有意义,这些是您需要遵循的步骤:

  1. 将角度源代码移动到弹簧启动应用程序
  2. 修改angular-cli.json文件,使资源中的输出目录为static或resources公共文件夹:"outDir":"../../resources/static/home"这将设置使用angular cli从命令行运行build的输出目录。
  3. 修改您登录的默认成功网址以重定向到/home/index.html .formLogin().defaultSuccessUrl("/home/index.html", true)这将强制成功登录以始终重定向到您的角应用。
  4. 如果您想自动将角度应用程序构建到maven,请将此插件添加到您的POM文件中:

        <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
                <execution>
                    <phase>generate-resources</phase>
                    <configuration>
                        <tasks>
                            <exec executable="cmd" osfamily="windows" dir="${project.basedir}/src/main/angular/ui">
                                <arg value="/c"/>
                                <arg value="ng"/>
                                <arg value="build"/>
                            </exec>
                            <exec executable="/bin/sh" osfamily="mac" dir="${project.basedir}/src/main/angular/ui">
                                <arg value="-c"/>
                                <arg value="ng build"/>
                            </exec>
                        </tasks>
                    </configuration>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    
  5. 如果您还有其他问题,我很乐意进一步回答,如果这还不足以让您发挥作用。

答案 3 :(得分:1)

您是否尝试明确允许OPTIONS请求?像这样:

protected void configure(HttpSecurity http) throws Exception {
   ...
   http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll();
   ...
}

答案 4 :(得分:1)

首先 - 这肯定是一个CORS配置问题,因为你在不同的地址上运行后端和前端。

你可以改变后端以支持CORS请求,但这是一个肮脏的解决方案,特别是因为你可能不想要你的应用程序的生产版本的CORS。 更简洁的方法是将前端或后端配置为服务器作为另一个的代理。

我更喜欢将前端配置为代理后端请求。这样它对应用程序的生产版本没有任何影响。

下一部分假设您正在使用angular-cli (正如您应该使用angular2 app)。

他们在专门针对代理配置的文档中有一个部分:https://www.npmjs.com/package/angular-cli#proxy-to-backend

我的所有REST服务都在 / api 上下文中,所以我的proxy.conf.json看起来像这样:

{
  "/api": {
    "target": "http://localhost:8083",
    "secure": false
  },
  "/login": {
    "target": "http://localhost:8083",
    "secure": false
  },
  "/logout": {
    "target": "http://localhost:8083",
    "secure": false
  }
}

http://localhost:8083是我的后端。然后,您可以使用以下命令运行应用程序:

ng serve --proxy-config proxy.conf.json

并仅使用您的前端地址。无需CORS配置。

P.S。如果你不使用angular-cli我相信你可以使用这种方法用你用来提供前卫的任何工具。在切换到angular-cli之前,我也使用了lite-server

相关问题