使用Spring Security进行Angular 2登录

5

我正在尝试将Spring Security与自定义的Angular 2登录集成,即我的应用程序的特定端点受到Spring Security保护,尝试访问它将重定向到在Angular 2中处理的/login。目前情况下,我不知道如何执行登录并在登录后授予对后端API的访问权限。

我正在按以下方式配置Spring Security:

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .cors().and()
        .authorizeRequests()
        .antMatchers("/api/someEndpoint/**")
        .hasRole(ADMIN_ROLE).and().formLogin()
        .loginPage("/login").and().logout();
}


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

由于我使用默认登录,一切都很顺利,但我发现自己无法创建一个可用的Angular 2登录集成。我尝试了以下Angular 2代码,但并没有成功:

login(loginDetails:Object) {
    console.log(loginDetails)
    const headers = new Headers({ 'Content-Type': 'application/json' });
const options = new RequestOptions({ headers: headers });
const body = JSON.stringify(loginDetails);
    console.log(headers);
    console.log(body);
return this.http.post(this.loginUrl, body, options) 
}

据我所知,Spring Security 的默认用户名和密码变量名为 "username" 和 "password",我确信它们是在请求体中传递的。因此,当传递一些无效的用户数据,例如 {"username":"admin", "password" : "pass"} 时,应该被重定向到 /login?error 或其他类似的页面。当成功验证后,应该重定向到 /welcome 并保持已验证状态。
我已经在我的数据库中定义了用户名和密码,并且我的自定义 userDetailsService 会对其进行检查。欢迎提出任何答案、评论或问题。

1
formLogin 需要 application/x-www-form-urlencoded 类型的参数,而不是 application/json。由于你正在使用 API,因此必须使用 HTTP 基本身份验证或 OAuth2。 - WildDev
我现在尝试的方式不是HTTP基本认证吗? - Fernando Castilla Ospina
实际上你不是。 - WildDev
3个回答

7

当你使用API时,必须使用HTTP基本身份验证。同时需要使用HTTPS来防止中间人攻击。

要在Angular中实现HTTP基本身份验证,登录服务应该如下所示:

login (loginDetails: any): Observable<LoginResponse> { // custom class, may be empty for now

    let headers = new Headers({ 
      'Authorization': 'Basic ' + btoa(loginDetails.login + ':' + loginDetails.pass),
      'X-Requested-With': 'XMLHttpRequest' // to suppress 401 browser popup
    });

    let options = new RequestOptions({ 
      headers: headers 
    });

    return this.http.post(this.loginUrl, {}, options)
      .catch(e => this.handleError(e)); // handle 401 error - bad credentials
}

...然后在调用组件中订阅它:

loginNow() {
   this
     .loginService
     .login(this.loginDetails)
     .subscribe(next => {
        this.router.navigateByUrl("/"); // login succeed
     }, error => {
        this.error = "Bad credentials"; // or extract smth from <error> object
     });
}

然后你可以在组件模板中使用loginNow()方法,例如(click)="loginNow()

一旦服务器接受授权,由于Spring Security的特性,JSESSIONID将自动存储到你的浏览器中,因此你不需要每次访问私有资源时都发送凭据。

你的登录服务器方法可能如下所示:

@PreAuthorize("hasRole('USER')")
@PostMapping("/login")
public ResponseEntity login() {
    return new ResponseEntity<>(HttpStatus.OK);
}

当授权失败时,它将拒绝并返回401未经授权,当授权成功时,它将接受并返回200成功

如何正确设置服务器?有多个Spring Security演示项目可供参考:https://github.com/spring-guides/tut-spring-security-and-angular-js


谢谢您的回答,我有一个问题:(哎呀,编辑一下): {"username":"admin", "password" : "pass"} 这是我要发送的字符串,我没有将用户名和密码分开,我从 formGroup.value 中获取它们。如何修改这行代码:'Authorization': 'Basic ' + btoa(loginDetails.login + ':' + loginDetails.pass)? - Fernando Castilla Ospina
最好的选择是放弃使用 formGroup,而是使用组件字段的双向绑定,例如:在 input 元素上指定 [(ngModel)]="this.username"。否则,你仍然需要解析它们,可能像这样 -> JSON.parse(this.loginDetails).username 等。 - WildDev
我将用户名和密码绑定到组件并将它们传递给登录服务,但即使输入了错误的凭据,它也总是重定向到欢迎页面,而且当我键入正确的凭据时,我也没有被验证,因为我无法访问受保护的端点。 - Fernando Castilla Ospina
请忽略我的上一条评论(我不会删除它,因为它可能对某些人有用),我忘记在Spring安全配置中启用HTTP基本认证。 - Fernando Castilla Ospina

0

你的Spring安全配置应该像这样

 http!!
     .cors().and()
     .csrf().disable()
    .authorizeRequests()
     .requestMatchers(object: RequestMatcher {
       override fun matches(request: HttpServletRequest?): Boolean {
         return CorsUtils.isCorsRequest(request)
       }
     }).permitAll()
    .antMatchers("/api/**").authenticated()
    .anyRequest().permitAll()
    .and()
    .formLogin().permitAll()

0

我遇到了类似的问题,但是我不得不重写成功注销处理程序,如此处所述。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接