Angular 5:从拦截器中的 http 响应标头获取授权
Posted
技术标签:
【中文标题】Angular 5:从拦截器中的 http 响应标头获取授权【英文标题】:Angular 5 : get authorization from http response headers in the interceptor 【发布时间】:2019-05-20 18:13:50 【问题描述】:一般来说,我是 Angular 的新手,我成功地使用带有 Spring Security 的 jwt 实现了身份验证。 但是现在我在到期后实现了刷新令牌,并且在检索我从后端的响应标头中发送的新刷新令牌时遇到了问题。
这里是负责令牌管理的 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 但是当我检查开发工具中的网络选项卡时,我可以看到它是由后端过滤器成功发送的。我错过了什么?
【问题讨论】:
【参考方案1】:要解决此问题,您必须像这样(对于 JAVA)从后端过滤器显式公开响应的标头:
response.addHeader("Access-Control-Expose-Headers",HEADER_STRING);
我希望对某人有所帮助。
【讨论】:
以上是关于Angular 5:从拦截器中的 http 响应标头获取授权的主要内容,如果未能解决你的问题,请参考以下文章
Angular 7 - 如何在某些响应状态代码上重试 http 请求?
使用 Angular 4.3 的 HttpInterceptor 拦截 HTTP 响应标头
Angular 4.3 - HTTP 拦截器 - 刷新 JWT 令牌
从 API 响应读取响应标头 - Angular 5 + TypeScript