如何将凭据从 Angular 前端传递到 Spring 后端(Spring Security 的基本身份验证)

Posted

技术标签:

【中文标题】如何将凭据从 Angular 前端传递到 Spring 后端(Spring Security 的基本身份验证)【英文标题】:How to pass credentials from Angular2 frontend to Spring backend (Basic-Authentification with Spring Security) 【发布时间】:2017-08-03 04:40:52 【问题描述】:

我正在开发一个使用 spring 作为后端angular2 作为前端的应用程序,后端是安全的(使用 Spring security),当我运行它时,我有默认的登录表单。我想从客户端登录到服务器端,但是当我尝试传递凭据时,我在浏览器控制台中出现了这些错误。

以下是错误:

我正在 Wildfly 服务器(与后端部分相同的端口)上部署我的 angular2 应用程序。

配置类

    @Configuration
    @EnableWebSecurity
    @ComponentScan(basePackages =  "tn.com.kmf.springdemo.config" )
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter 


        @Autowired
        private RESTAuthenticationFailureHandler authenticationFailureHandler;
        @Autowired
        private RESTAuthenticationSuccessHandler authenticationSuccessHandler;

        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
            auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN", "USER").and().withUser("user")
                    .password("user").roles("USER");
        

        @Override
        protected void configure(HttpSecurity http) throws Exception 

           http
    .csrf().disable()
    .addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class)
    .authorizeRequests()
    .antMatchers("/etudiant/list").hasRole("ADMIN")
        .anyRequest().authenticated()
        .and()
    .formLogin()
        .loginProcessingUrl("/login")
        .usernameParameter("username")
        .passwordParameter("password")
        .successHandler(authenticationSuccessHandler)
        .failureHandler(authenticationFailureHandler)
    .permitAll()
    .and()
    .httpBasic();
        

    

CorsFilter:

public class CORSFilter implements Filter


       @Override
       public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException 
          HttpServletResponse response = (HttpServletResponse) res;
          HttpServletRequest request = (HttpServletRequest) req;
          response.setHeader("Access-Control-Allow-Origin", "*");
          response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
          response.setHeader("Access-Control-Max-Age", "3600");
          response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization");

          if ("OPTIONS".equalsIgnoreCase(request.getMethod())) 
              response.setStatus(HttpServletResponse.SC_OK);
           else 
              chain.doFilter(req, res);
         
     

      @Override
      public void init(FilterConfig filterConfig) 

      @Override
      public void destroy()  

web.xml 中的安全过滤器:

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>
        org.springframework.web.filter.DelegatingFilterProxy
        </filter-class>
  </filter>
  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

angular2侧的登录表单:

<div class="login jumbotron center-block">
   <h1>Login</h1>
   <form role="form" (submit)="login(username.value, password.value)">
   <div class="form-group">
     <label for="username">Username</label>
     <input type="text" #username class="form-control" id="username" placeholder="Username">
   </div>
   <div class="form-group">
     <label for="password">Password</label>
     <input type="password" #password class="form-control" id="password" placeholder="Password">
   </div>
   <button type="submit" class="btn btn-default">Submit</button>
 </form>
 </div>

服务代码:(login.service.ts)

   import  Injectable  from '@angular/core';

import  Http, Response, Headers  from '@angular/http';
import  Observable  from 'rxjs/Rx';

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

@Injectable()
export class LoginService 

  isAuthenticated = false;

  constructor(private http: Http) 

  

  login(username: string, password: string) 
    const headers = new Headers();
    const creds = 'username=' + username + '&password=' + password;
   // headers.append('Content-Type', 'application/json');
    headers.append('Authorization', 'Basic ' +
      btoa(username+':'+password));
    headers.append('Content-Type', 'application/x-www-form-urlencoded');
    return new Promise((resolve) => 
      this.http.post('http://localhost:8080/StudentManager/login', creds,  headers: headers ).subscribe((data) => 
        console.log(creds);
        if (data.json().success) 
          // window.localStorage.setItem('auth_key', data.json().token);
          // this.userId = data.json().userId;
          this.isAuthenticated = true;
        
        resolve(this.isAuthenticated);
      );
    );
  

【问题讨论】:

如果你想使用表单登录,你必须发送内容类型为application/x-www-form-urlencoded而不是JSON的用户名和密码。 我把它改成了 application/x-www-form-urlencoded 但我仍然有同样的错误 不,我只是发送没有 JSON.stringify 的凭据 我重建它,现在我认为它可以工作,因为我有状态代码:200 ok,但为什么我有这个错误EXCEPTION: Unexpected end of JSON input 以及如何修复它?谢谢你对我这么有耐心 响应正文中没有html 【参考方案1】:

当你声明这个时:

.and().formLogin()

默认登录端点应该是:" yourAppContextPath/login?user=myUser&password=myPassword " ;

当你声明时:

.and().httpBasic();

表示你要使用spring“基本访问认证”

所以登录后你应该每次都发送一个值为“Basic :”的标题

    var authenticate = function(credentials, callback) 
        var headers = credentials ? 
            authorization : "Basic "
                    + btoa(credentials.username + ":"
                            + credentials.password)
         : ;
        $http.get('user', 
            headers : headers
        ).then(function(response) 
            if (response.data.name) 
                $rootScope.authenticated = true;
             else 
                $rootScope.authenticated = false;
            
            callback && callback($rootScope.authenticated);
        , function() 
            $rootScope.authenticated = false;
            callback && callback(false);
        );
    

有完整的示例可能对您有所帮助: https://github.com/spring-guides/tut-spring-security-and-angular-js/blob/master/single/src/main/resources/static/js/hello.js

您也在使用 CORS!所以你必须在你的登录页面中添加一个 _cors 令牌。

【讨论】:

以上是关于如何将凭据从 Angular 前端传递到 Spring 后端(Spring Security 的基本身份验证)的主要内容,如果未能解决你的问题,请参考以下文章

如何将数据从 AngularJS 前端传递到 Nodejs 后端?

从服务传递到组件的角度错误消息

将错误消息从 SSE (Webflux) Spring Boot 应用程序传递到 Angular 7 前端

如何使用 JWT 从前端(角度 4)将密钥传递到后端(节点 js)

如何从 django rest 框架访问 jwt 令牌到 Angular 前端

如何将 Windows 身份验证凭据从客户端传递到 Web API 服务