简体   繁体   中英

Spring Boot + Angular 2 + JWT

I'm using Angular 2 and trying to send a header parameters on get method. I have following code:

let tokenUrl2 = "http://localhost:8080/users";

let headers = new Headers();
headers.append('abc', token);

let options = new RequestOptions({ headers: headers });
return this.http.get(tokenUrl2, options);

I get this error:

Response for preflight is invalid (redirect)

I also added these parameters but I get the same error:

headers.append('Content-Type', 'application/json');
headers.append('Authorization':'Bearer ' + token);

Could anyone tell me what is the right way to send header parameters. Thank you very much :)

EDIT 1

The URL " http://localhost:8080/users " is from spring boot web app (It's a REST service). I'm trying to communicate angular app with spring boot app. My idea is send it a token previousy obtained from another REST service. In a first REST service I got a token. This's a REST service with POST and It works. Then, I send this token to second REST Service ( http://localhost:8080/users ) through GET method. This second part doesn't work. I tried changed GET method to POST because on the firs part with a POST method do work but this new change neither work. I still getting the same message: Response for preflight is invalid (redirect) .

I have these questions:

If my first REST service (implemented with POST) works, why the second doesn't? This is because my request (my angular app) doesn't implement CORS?

I hope wiht this details, you could help me to solve my problem.

Thanks!

EDIT 2

Initially, I thought that I only had an error when send headers parameters to REST service from Angular app. But, I've investigated and I see that my problem envolves more components. I tell about my components:

I have an angular app that need consuming a REST service from Spring boot app. Every REST service need authentication and for that I use JWT.

First. My angular app consumes a authenticate REST service. If this done. REST service return a token.

Second. With this token, angular app can consume another REST services secured with Spring Security.

My error happens on second step. I can't consume others services. I have a custom filter that extends from OncePerRequestFilter and even is not invoked. In angular app I get a message that I reported before:

Response for preflight is invalid (redirect)

As I said in my previous edit. I don't uderstand because the first REST service is invoked but second doesn't. Neither I understand why my custom filter is not invoked. I think that I'm invoking wrong from angular app.

My code:

Custom Filter:

public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private final Log logger = LogFactory.getLog(this.getClass());

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Value("${jwt.header}")
    private String tokenHeader;

    static final String ORIGIN = "Origin";

    @Override
    //@CrossOrigin(origins = "*")
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        logger.info("checking authentication für user ");

        String authToken = request.getHeader(this.tokenHeader);

        // authToken.startsWith("Bearer ")
        // String authToken = header.substring(7);
        String username = jwtTokenUtil.getUsernameFromToken(authToken);

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            // It is not compelling necessary to load the use details from the database. You could also store the information
            // in the token and read it from it. It's up to you ;)
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

            // For simple validation it is completely sufficient to just check the token integrity. You don't have to call
            // the database compellingly. Again it's up to you ;)
            if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                logger.info("authenticated user " + username + ", setting security context");
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }

        filterChain.doFilter(request, response);
    }
}

Angular Controller and Service:

import { Component, OnInit } from '@angular/core';
import { LoginService } from './login.service';

interface TokenJson {
    token: string;
}


@Component({
    selector: 'login',
    templateUrl: 'login.component.html',
    styleUrls: ['login.scss'],
    providers: [LoginService]
})
export class LoginComponent implements OnInit {
    private model = {'username':'****', 'password':'****'};
    private currentToken:string ;
    private tokenJson: TokenJson;

    // constructor
    constructor(private _loginService: LoginService) {

    }

    // on-init
    ngOnInit() {
      debugger;
      this._loginService.sendCredential(this.model).subscribe(
         data => {
                   debugger;
                   //localStorage.setItem("token", JSON.parse(JSON.stringify(data)).token);

                  // this.currentToken = JSON.parse(JSON.stringify(data))._body;
                   this.tokenJson = JSON.parse(JSON.stringify(data))._body;

                   this.currentToken = JSON.parse(JSON.parse(JSON.stringify(data))._body).token;
                   localStorage.setItem("token",  this.currentToken);
                   this._loginService.sendToken(localStorage.getItem("token")).subscribe(
                     data => {
                               //this.currentUserName=this.model.username;
                               //localStorage.setItem("currentUserName", this.model.username);
                               debugger;
                               this.model.username='';
                               this.model.password='';
                             },
                     error => {
                       debugger;
                       console.log(error)
                     }
                   );
                 },
         error => {
           debugger;
           console.log(error)
         }
       );
    }


}

import {Injectable} from "@angular/core";
import {Http, Headers, Response, RequestOptions} from '@angular/http';
import {Observable}     from 'rxjs/Observable';


@Injectable()
export class LoginService {
  token: string;

  constructor (private http: Http) {}

  sendCredential(model) {
    debugger;

    let tokenUrl1 = "http://localhost:8080/auth";
    let headers = new Headers();
    headers.append('Content-Type', 'application/json');
    return this.http.post(tokenUrl1, JSON.stringify(model), {headers: headers});
  }

  sendToken(token) {
    debugger;
    let tokenUrl2 = "http://localhost:8080/users";
    console.log('Bearer '+token);


    let getHeaders2 = new Headers();
    getHeaders2.append('Authorization', token);

    let headers = new Headers();

    headers.append('authorization', token);



    return this.http.post(tokenUrl2, {}, options);
  }



}

SOLUTION

I've found the problem. The error message got for request of type OPTIONS. I had only avoid that type of requests. I added following configuration to security configuration class:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    }
    //

}

PD: I guided from here

 Response for preflight is invalid (redirect)

That sounds more like your server isn't handling the pre-flight request properly.

Are you using CORS? (ie are you using ng serve to run your Angular app, but trying to access a server on a different port/machine?).

Because, based on the error you're getting, I think your backend server isn't set up to handle CORS properly.

    let requestOptions = new RequestOptions({
        headers: new Headers({
            'Content-Type': 'application/json'
        }),
        method: RequestMethod.Post,
        body: {}
    });

    this.http.request("http://localhost:8080/users", requestOptions)
        .map((response: Response) => response.json())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM