角度5:从拦截器中的http响应标头获取授权

时间:2018-12-20 08:37:07

标签: angular spring-security jwt

我通常对angular还是陌生的,我成功地使用了jwt和spring security实现了身份验证。 但是现在我要在过期后实现刷新令牌,并且在检索从后端在响应标头中发送的新刷新令牌时遇到了问题。

这是负责令牌管理的spring安全过滤器:

package ma.dataprotect.extend.cqradar.web.security;

import static ma.dataprotect.extend.cqradar.ws.security.utils.SecurityConstants.HEADER_STRING;
import static ma.dataprotect.extend.cqradar.ws.security.utils.SecurityConstants.ROLES;
import static ma.dataprotect.extend.cqradar.ws.security.utils.SecurityConstants.SECRET;
import static ma.dataprotect.extend.cqradar.ws.security.utils.SecurityConstants.TOKEN_PREFIX;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import ma.dataprotect.extend.cqradar.commun.model.User;
import ma.dataprotect.extend.cqradar.ws.security.utils.SecurityConstants;
import ma.dataprotect.extend.cqradar.ws.security.utils.TokenUtility;

public class JWTAuthorizationFilter extends BasicAuthenticationFilter {

    private TokenUtility tokenUtility;

    public JWTAuthorizationFilter(AuthenticationManager authenticationManager, TokenUtility tokenUtility) {
        super(authenticationManager);
        this.tokenUtility = tokenUtility;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        String header = request.getHeader(HEADER_STRING);

        if (header == null || !header.startsWith(TOKEN_PREFIX)) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setHeader("content-type", MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
            response.getWriter().write(SecurityConstants.convertObjectToJson(
                    "No '" + HEADER_STRING + "' header or does not start with '" + TOKEN_PREFIX + "'"));
            // chain.doFilter(request, response);
            return;
        }

        UsernamePasswordAuthenticationToken authenticationToken = null;
        try {
            authenticationToken = getAuthentication(header);
        } catch (ExpiredJwtException e) {
            User result = tokenUtility.refreshToken(header); // refresh Token 
            if (result == null) {// this user needs to be logged out
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                response.setHeader("content-type", MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
                response.getWriter().write(SecurityConstants.convertObjectToJson("JWT expired needs login"));
                return;
            } else {// send the refreshed token in the headers
                String refreshedToken = TOKEN_PREFIX.concat(result.getToken());
                response.setStatus(HttpServletResponse.SC_OK);
                response.setHeader("content-type", MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
                response.addHeader(HEADER_STRING, refreshedToken); // Here !!!!
                authenticationToken = getAuthentication(refreshedToken);
            }
        }
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        chain.doFilter(request, response);
    }

    @SuppressWarnings("unchecked")
    private UsernamePasswordAuthenticationToken getAuthentication(String token) {
        if (token == null)
            return null;

        Claims claims = Jwts.parser().setSigningKey(SECRET.getBytes()).parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                .getBody();
        String user = claims.getSubject();
        if (user == null)
            return null;

        List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
        authorities.add(new SimpleGrantedAuthority(
                ((ArrayList<LinkedHashMap<String, String>>) claims.get(ROLES)).get(0).get("authority")));

        return new UsernamePasswordAuthenticationToken(user, null, authorities);
    }

}

这是前端的拦截器,应该从响应中检索标头:

import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse, HttpResponse} from "@angular/common/http";
import {Observable} from "rxjs/Observable";
import {Injectable} from "@angular/core";
import {AuthHelperService} from "./auth-helper.service";
import 'rxjs/add/operator/do';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  token;

  constructor(private authHelper: AuthHelperService) {
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.token = this.authHelper.getToken();

    if(this.token) {     
      return next.handle(this.addTokenToRequest(req, this.token)).do(
        (event: HttpEvent<any>) => {// catching the http response 
          if (event instanceof HttpResponse) {
            let resp : HttpResponse<any> = event;
            console.log("Success : intercept called req headers:",resp.headers.get('Authorization')); // always gives null in case of refresh (no token !! the Big Problem !!)
            console.log(this.authHelper.getToken());
            if(resp.headers.get('Authorization')!=null){// not null in case of refresh 
              this.authHelper.setToken(resp.headers.get('Authorization')); // reset the new token here to be used in next http requests
            }            
            return next.handle(this.addTokenToRequest(req, this.authHelper.getToken()));
          }
      },
        (err: any) => {
          if (err instanceof HttpErrorResponse) {
            switch ((<HttpErrorResponse>err).status) {
              case 400:
                this.authHelper.logout();
                return next.handle(req) ;
              default :
                return next.handle(this.addTokenToRequest(req, this.token));  
            }
          }
        }
      );
    }
    return next.handle(this.addJsonToRequest(req));
  }

  private addTokenToRequest(request: HttpRequest<any>, token: string) : HttpRequest<any> {
    return request.clone({ headers: request.headers.append("Authorization", token).append("Content-Type","application/json")});
  }

  private addJsonToRequest(request: HttpRequest<any>) : HttpRequest<any> {
    return request.clone({ headers: request.headers.append("Content-Type", "application/json")});
  }

}

编辑了auth-helper.service.ts的添加代码:

import { Injectable, OnDestroy } from "@angular/core";
import { Router } from "@angular/router";
import { User } from "../../models/User";

@Injectable()
export class AuthHelperService implements OnDestroy { 

  private tokenKey: string;
  private userKey: string;
  private roleKey: string;
  private userIdKey: string;
  private userStateKey : string;

  constructor(private router: Router) {
    this.tokenKey = 'id_token';
    this.userKey = 'user_full_name';
    this.roleKey = 'user_role';
    this.userIdKey = 'user_id';
    this.userStateKey = 'user';
    this.forceNavToProf()
    console.log("AuthHelperService constructor called");
  }

  forceNavToProf(){
    this.router.events.subscribe((event) => {
      if(this.isLoggedIn()){
       if(this.isNew()) {
        if(event['url'] != undefined && event['url'] != '/logout' && event['url'] != '/profile'){
          this.router.navigate(['/profile']);
        }        
       }
      }
    });
  }

  ngOnDestroy(){
    console.log('AuthHelperService ngOnDestroy called')
    sessionStorage.removeItem(this.tokenKey);
    sessionStorage.removeItem(this.userKey);
    sessionStorage.removeItem(this.roleKey);
    sessionStorage.removeItem(this.userIdKey);
    sessionStorage.removeItem(this.userStateKey);
    this.router.navigate(['/login']);
  }

  setSession(loggedInUser: User) {
    sessionStorage.setItem(this.tokenKey, 'Bearer ' + loggedInUser.token);
    sessionStorage.setItem(this.userKey, loggedInUser.firstName+' '+loggedInUser.lastName);
    sessionStorage.setItem(this.roleKey, loggedInUser.role.roleName);
    sessionStorage.setItem(this.userIdKey, loggedInUser.id); 
    sessionStorage.setItem(this.userStateKey, String(loggedInUser.isNew));
    this.homePage(loggedInUser.isNew);
    console.log('session values are set correctly ');
  }

  setSessionAfterRefresh(loggedInUser: User) {
    sessionStorage.setItem(this.tokenKey, 'Bearer ' + loggedInUser.token);
    sessionStorage.setItem(this.userKey, loggedInUser.firstName+' '+loggedInUser.lastName);
    sessionStorage.setItem(this.roleKey, loggedInUser.role.roleName);
    sessionStorage.setItem(this.userIdKey, loggedInUser.id); 
    sessionStorage.setItem(this.userStateKey, String(loggedInUser.isNew)); 
    console.log('session values are set correctly ',this.router.url);   
  }

   // Solution de depanage pour debloquer 
  refresh(){   
    console.log('refresh ',this.router.url);
    let currentUrl = this.router.url;
    this.router.navigate(['']);
    setTimeout(() => { this.router.navigate([currentUrl]); }, 100 );

  }

  homePage(userState:boolean){
    if(userState){
      this.router.navigate(['/profile']);
    }else{
      this.router.navigate(['/']);
    }    
  }

  logout() {
    console.log('AuthHelperService logout called');
    sessionStorage.removeItem(this.tokenKey);
    sessionStorage.removeItem(this.userKey);
    sessionStorage.removeItem(this.roleKey);
    sessionStorage.removeItem(this.userIdKey);
    sessionStorage.removeItem(this.userStateKey);
    this.router.navigate(['/login']);
  }

  public isLoggedIn(): boolean {
    return this.getToken() != null;
  }  

  public getLoggedInUserName(): string{
    return sessionStorage.getItem(this.userKey);
  }

  public getLoggedInUserID(): number{
    return Number(sessionStorage.getItem(this.userIdKey));
  }

  public isNew(): boolean{
    return sessionStorage.getItem(this.userStateKey) === "true";
  }

  public setToken(token : string) {
    sessionStorage.setItem(this.tokenKey, token);
  }

  public getToken(): string {
    return sessionStorage.getItem(this.tokenKey);
  }

  public getIsAdmin(): boolean {
    return 'ROLE_ADMINISTRATOR' === sessionStorage.getItem(this.roleKey);
  }
}

在拦截器的代码中,它给出了null,但是当我在开发工具中检查network选项卡时,我可以看到它是由后端过滤器成功发送的。我想念什么?

enter image description here

1 个答案:

答案 0 :(得分:0)

要解决此问题,您必须像这样(对于JAVA)从后端过滤器显式公开响应的标头:

response.addHeader("Access-Control-Expose-Headers",HEADER_STRING);

我希望能对某人有所帮助。