Reputation: 1489
I created an API that handles users and if authentication is successful, I do return user information to front end using http servlet response:
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult) {
// String userName = ((User) authResult.getPrincipal()).getUsername();
String userName = ((UserPrincipalManager) authResult.getPrincipal()).getUsername();
String[] claims = getClaimsFromUser((UserPrincipalManager) authResult.getPrincipal());
String token = JWT.create()
.withIssuer(SecurityConstants.TOKEN_ISSUER)
.withAudience(SecurityConstants.TOKEN_AUDIENCE)
.withIssuedAt(new Date())
.withSubject(userName)
.withArrayClaim(SecurityConstants.AUTHORITIES, claims)
.withExpiresAt(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
.sign(Algorithm.HMAC256(SecurityConstants.getTokenSecret().getBytes()));
UserService userService = (UserService) SpringApplicationContext.getBean("userServiceImpl");
UserDto userDto = userService.getUser(userName);
try {
UserResponse userResponse = new ModelMapper().map(userDto, UserResponse.class);
response.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
response.addHeader(SecurityConstants.USER_ID, userDto.getUserId());
response.getWriter().write(new ObjectMapper().writeValueAsString(userResponse));
response.getWriter().flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
As I use spring security, I implemented the following configuration:
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
// Configure AuthenticationManagerBuilder
AuthenticationManagerBuilder authenticationManagerBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
AuthenticationManager authenticationManager = authenticationManagerBuilder.build();
// Create AuthenticationFilter
AuthenticationFilter authenticationFilter = getAuthenticationFilter(authenticationManager);
IpAddressMatcher hasIpAddress = new IpAddressMatcher(Objects.requireNonNull(environment.getProperty("gateway.ip")));
IpAddressMatcher hasIpv4Address = new IpAddressMatcher(Objects.requireNonNull(environment.getProperty("gateway.ipv4")));
return http
//.cors(AbstractHttpConfigurer::disable)
//.cors(corsCustomizer -> corsCustomizer.configurationSource(corsConfigurationSource()))
.cors(Customizer.withDefaults())
//.csrf((csrf) -> csrf.disable())
.csrf(AbstractHttpConfigurer::disable)
// Aucune url n'est disponible sans passer par le gateway si le gateway est sur un autre serveur
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.requestMatchers("/**")
.access((authentication, context) -> getAccess(authentication, context, hasIpAddress, hasIpv4Address))
// on autorise tous les points d'entrée d'actuator
.requestMatchers(new AntPathRequestMatcher("/actuator/**"))
.access((authentication, context) -> getAccess(authentication, context, hasIpAddress, hasIpv4Address))
.requestMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL)
.access((authentication, context) -> getAccess(authentication, context, hasIpAddress, hasIpv4Address))
.requestMatchers(HttpMethod.GET, SecurityConstants.EMAIL_VERIFICATION_URL)
//.permitAll()
.access((authentication, context) -> getAccess(authentication, context, hasIpAddress, hasIpv4Address))
.requestMatchers(HttpMethod.POST, SecurityConstants.PASSWORD_RESET_REQUEST_URL)
.access((authentication, context) -> getAccess(authentication, context, hasIpAddress, hasIpv4Address))
//.permitAll()
.requestMatchers(HttpMethod.POST, SecurityConstants.PASSWORD_RESET_URL)
.access((authentication, context) -> getAccess(authentication, context, hasIpAddress, hasIpv4Address))
//.permitAll()
.requestMatchers("/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**", "/v3/api-docs/**").permitAll()
// on peut appeler cette url directement
.requestMatchers(HttpMethod.GET, "/hello")
.access((authentication, context) -> getAccess(authentication, context, hasIpAddress, hasIpv4Address))
//.permitAll()
//.anyRequest().access((authentication, context) -> {
// if(authentication.get().isAuthenticated())
// return getAccess(authentication, context, hasIpAddress, hasIpv4Address);
// return new AuthorizationDecision(false);
//})
)
.addFilterAfter(authenticationFilter, AuthenticationFilter.class)
.addFilterAfter(new AuthorizationFilter(authenticationManager, jwtTokenProvider), AuthorizationFilter.class)
.authenticationManager(authenticationManager)
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
}
private AuthorizationDecision getAccess(
Supplier<Authentication> authentication,
RequestAuthorizationContext context,
IpAddressMatcher hasIpAddress,
IpAddressMatcher hasIpv4Address) {
if(hasIpAddress.matches(context.getRequest()))
return new AuthorizationDecision(
hasIpAddress.matches(context.getRequest()));
return new AuthorizationDecision(
hasIpv4Address.matches(context.getRequest()));
}
// customize login URL
public AuthenticationFilter getAuthenticationFilter(AuthenticationManager authenticationManager) {
final AuthenticationFilter filter = new AuthenticationFilter(authenticationManager, servletContext);
filter.setFilterProcessesUrl(SecurityConstants.SIGN_IN_URL);
return filter;
}
// cors configuration source
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(List.of("*"));
//configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedMethods(List.of("GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS", "HEAD"));
configuration.setAllowedHeaders(List.of("Content-Type", "Authorization", "Origin", "x-access-token", "XSRF-TOKEN", "userId"));
configuration.addAllowedHeader("Authorization");
configuration.addAllowedHeader("userId");
configuration.addExposedHeader("Authorization");
configuration.addExposedHeader("userId");
configuration.setMaxAge(3600L);
configuration.setAllowCredentials(Boolean.TRUE);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
I also added cors
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.exposedHeaders("Authorization", "userId")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS", "HEAD")
.allowedHeaders("Content-Type", "Authorization", "Origin", "x-access-token", "XSRF-TOKEN", "userId")
.exposedHeaders("Authorization", "userId")
.maxAge(3600L)
.allowCredentials(Boolean.TRUE);
}
}
So far, so good... In front end I have a login component (using angular 17). My submit button, is just redirecting user after authentication.
submit() { this.hasError = false; // this.router.navigate(['../admin/dashboard']) const loginSubscr = this.authService .login(this.f.email.value, this.f.password.value) .pipe(first()) .subscribe((user: UserModel | undefined) => { if (user) { this.router.navigate([this.returnUrl]); } else { this.hasError = true; } }); this.unsubscribe.push(loginSubscr); }
My auth service is retrieving the response from server and store data in local storage...
login(email: string, password: string): Observable<UserType> {
this.isLoadingSubject.next(true);
return this.authHttpService.login(email, password)
.pipe(
map((auth: any) => {
console.log("map here")
const result = this.setAuthFromLocalStorage(auth);
return result;
}),
switchMap(() => this.getUserByToken()),
catchError((err) => {
console.error('err', err);
return of(undefined);
}),
finalize(() => this.isLoadingSubject.next(false))
);
}
Now, my http request is a simple post :
login(email: string, password: string): Observable<any> {
const headers = new HttpHeaders(
{ 'Content-Type': 'application/json' }
);
//const observe = "response" as 'body';
const observe = "response";
const responseType = "json";
const options: object = {
headers: headers,
observe: observe,
responseType: responseType
};
let body = {
'email': email,
'password': password
}
return this.http.post<AuthModel>(API_USERS_URL + "/login", body, options);
}
Now my issue is the following:
But when reading data in network console in Chrome, the headers are correctly retrieved:
Upvotes: 0
Views: 182
Reputation: 1149
You can't see headers of you http call like it on chrome console network tab, because differents headers not available in HttpHeaders method as field ( Even less not public field ). you need to call a method to get them. try this in service call subscriber to get all http headers name: resp => resp.headers.keys()
.
Another way, if you want to get as example Authorization header value use resp.headers.get('Authorization')
. Hope this could be helpful.
Upvotes: 2