2

I am trying to implement JWT Token Based Authentication and Authorization. I am using Spring Boot for backend and Angular 7 as frontend and my job is to complete backend work. Bearer Token is successfully generated in Authentication. And I have added it into Header but when I try to fetch the Header using request.getHeader(HEADER_STRING) it isnull.

So how to use this generated Token for further identifying logged in user OR it is frontend job to identify user after token generation?

I have used custom login page in Spring Security and when I make a request to http://localhost:8080/login it contains two login forms instead of one.

Image

Console

doFilterInternal header null response.getHeader(HEADER_STRING) null getAuthenticationToken token: null doFilterInternal authenticationToken : null null doFilterInternal header null response.getHeader(HEADER_STRING) null getAuthenticationToken token: null doFilterInternal authenticationToken : null null doFilterInternal header null response.getHeader(HEADER_STRING) null getAuthenticationToken token: null doFilterInternal authenticationToken : null null doFilterInternal header null response.getHeader(HEADER_STRING) null getAuthenticationToken token: null doFilterInternal authenticationToken : null null attemptAuthentication email: [email protected] abcd@A123 2019-03-04 11:33:34.320 INFO 11652 --- [nio-8080-exec-5] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory Hibernate: select user0_.id as id1_1_, user0_.branch as branch2_1_, user0_.contact as contact3_1_, user0_.createtime as createti4_1_, user0_.designation as designat5_1_, user0_.email as email6_1_, user0_.expreiance as expreian7_1_, user0_.name as name8_1_, user0_.password as password9_1_, user0_.role as role10_1_, user0_.skypeid as skypeid11_1_, user0_.statusenable as statuse12_1_ from user user0_ where user0_.email=? loadUserByUsername User [user.toString] Role: ROLE_ADMIN successfulAuthentication username [email protected] successfulAuthentication bearer Token Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJycEBjeWduZXRpbmZvdGVjaC5jb20iLCJleHAiOjE1NTE3NjU4MTR9.9mLS64W6JBS1RqlEKl1Zmjb8YS03E9k92ITkaFmw35JH4ELIua8Tbkzj0r9crDgdQnxm3YvFKAD9lY3cgoQsNw doFilterInternal header null response.getHeader(HEADER_STRING) null getAuthenticationToken token: null doFilterInternal authenticationToken : null null doFilterInternal header null response.getHeader(HEADER_STRING) null getAuthenticationToken token: null doFilterInternal authenticationToken : null null doFilterInternal header null response.getHeader(HEADER_STRING) null getAuthenticationToken token: null doFilterInternal authenticationToken : null null 

JWTAuthenticationFilter.java

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private AuthenticationManager authenticationManager; public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } @Autowired CustomUserDetailService customUserDetailService; @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try { System.out.println("attemptAuthentication "+" email: "+request.getParameter("email") + " "+request.getParameter("password")); //User user = new ObjectMapper().readValue(request.getInputStream(), User.class); return this.authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(request.getParameter("email"), request.getParameter("password"))); } catch (Exception e) { throw new RuntimeException(e); } } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { String username = ((org.springframework.security.core.userdetails.User) authResult.getPrincipal()).getUsername(); System.out.println("successfulAuthentication "+" username "+username); String token = Jwts .builder() .setSubject(username) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET) .compact(); String bearerToken = TOKEN_PREFIX + token; System.out.println("successfulAuthentication bearer Token "+bearerToken); response.getWriter().write(bearerToken); response.addHeader(HEADER_STRING, bearerToken); response.sendRedirect(SIGN_UP_SUCCESS); } } 

JWTAuthorizationFilter.java

public class JWTAuthorizationFilter extends BasicAuthenticationFilter { private final CustomUserDetailService customUserDetailService; public JWTAuthorizationFilter(AuthenticationManager authenticationManager, CustomUserDetailService customUserDetailService) { super(authenticationManager); this.customUserDetailService = customUserDetailService; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String header = request.getHeader(HEADER_STRING); System.out.println("doFilterInternal header "+header+ " response.getHeader(HEADER_STRING) "+response.getHeader(HEADER_STRING)); if (header == null || !header.startsWith(TOKEN_PREFIX)) { chain.doFilter(request, response); } UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(request); SecurityContextHolder.getContext().setAuthentication(authenticationToken); response.addHeader(header, SIGN_UP_URL); System.out.println("doFilterInternal authenticationToken : "+authenticationToken+ " "+response.getHeader(SIGN_UP_URL)); chain.doFilter(request, response); } private UsernamePasswordAuthenticationToken getAuthenticationToken(HttpServletRequest request) { String token = request.getHeader(HEADER_STRING); System.out.println("getAuthenticationToken token: "+token); if (token == null) return null; String username = Jwts.parser().setSigningKey(SECRET) .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) .getBody() .getSubject(); UserDetails userDetails = customUserDetailService.loadUserByUsername(username); System.out.println("getAuthenticationToken userDetails "+userDetails.toString()+ " userDetails.getAuthorities() "+userDetails.getAuthorities()); return username != null ? new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()) : null; } } 

CustomUserDetailService.java

@Component public class CustomUserDetailService implements UserDetailsService { private final UserRepository userRepository; @Autowired public CustomUserDetailService(UserRepository userRepository) { this.userRepository = userRepository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByEmail(username); if(user==null) { new UsernameNotFoundException("User not found"); return null; } else { System.out.println("loadUserByUsername "+user.toString()+" Role: "+user.getRole()); List<GrantedAuthority> authorityListAdmin = AuthorityUtils.createAuthorityList("ROLE_USER", "ROLE_ADMIN"); List<GrantedAuthority> authorityListUser = AuthorityUtils.createAuthorityList("ROLE_USER"); return new org.springframework.security.core.userdetails.User (user.getEmail(), user.getPassword(), user.getRole()=="ADMIN" ? authorityListAdmin : authorityListUser); } } } 

SecuriyConfig.java

@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer{ @Autowired private CustomUserDetailService customUserDetailService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserDetailService).passwordEncoder(new BCryptPasswordEncoder()); } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/home").setViewName("home"); registry.addViewController("/").setViewName("login"); registry.setOrder(Ordered.HIGHEST_PRECEDENCE); registry.addViewController("/login").setViewName("login"); registry.setOrder(Ordered.HIGHEST_PRECEDENCE); registry.addViewController("/failure").setViewName("failure"); registry.addViewController("/403").setViewName("403"); } @Override protected void configure(HttpSecurity http) throws Exception{ http .csrf().disable() .authorizeRequests().antMatchers("/login","/home","/failure").permitAll() .antMatchers(HttpMethod.POST,"/admin/**").permitAll()//hasRole("ADMIN") .antMatchers(HttpMethod.PUT,"/admin/**").hasRole("ADMIN") .antMatchers(HttpMethod.GET,"/admin/**").hasRole("ADMIN") .antMatchers(HttpMethod.GET,"/user/**").hasAnyRole("ADMIN","USER") .antMatchers(HttpMethod.POST,"/user/**").hasAnyRole("ADMIN","USER") .anyRequest().authenticated() .and() .addFilter(new JWTAuthenticationFilter(authenticationManager())) .addFilter(new JWTAuthorizationFilter(authenticationManager(), customUserDetailService)) .exceptionHandling().accessDeniedPage("/403") .and() .formLogin() .loginPage("/login") .loginProcessingUrl("/login") .usernameParameter("email") .passwordParameter("password") .defaultSuccessUrl("/home",true) .failureUrl("/failure") .and() .logout().logoutUrl("/logout").permitAll(); } public SecurityConfig(UserDetailsService userDetailsService) { super(); this.customUserDetailService = customUserDetailService; } } 

SecurityConstants.java

public class SecurityConstants { static final String SECRET = "Romil"; static final String TOKEN_PREFIX = "Bearer "; static final String HEADER_STRING = "Authorization"; static final String SIGN_UP_URL = "/login"; static final String SIGN_UP_SUCCESS = "/home"; static final long EXPIRATION_TIME = 86400000L; } 
10
  • i see this line response.sendRedirect(SIGN_UP_SUCCESS); meaning the context is lost and the request, response objects are new with the redirect Commented Mar 4, 2019 at 8:10
  • @resatz Thank you for the response. if I don't use response.sendRedirect(SIGN_UP_SUCCESS); then generated token is downloaded automatically in the system and login page stay as it is. Commented Mar 4, 2019 at 8:35
  • You can try using forward instead of redirect more details here stackoverflow.com/questions/2047122/… Commented Mar 4, 2019 at 8:42
  • @resatz RequestDispatcher will hold the current req, res with it and forwards to next URL. I have used RequestDispatcher rd = request.getRequestDispatcher(SIGN_UP_SUCCESS); rd.forward(request, response); and it again stays to the same page. console prints Request method 'POST' not supported Commented Mar 4, 2019 at 9:35
  • You are setting the authorization header on the client right? Before sending the request to the server. Commented Mar 5, 2019 at 0:57

1 Answer 1

3

How to Pass Token?

  • Generate the Token in Spring Boot On Successful Login send token as Response to front-end
  • Create AuthInterceptor in Angular that which will set the Token on every further request.

Notes:

  1. We also have to set WebConfig file according to our requirements.

    • allowedMethods
    • allowedHeaders
    • exposedHeaders
    • maxAge etc..
  2. For the request in which we don't need to Authenticate and Authorize the user we can add that APIs in ignoring().antMatchers("").

 @Override public void configure(WebSecurity web) throws Exception { web .ignoring() .antMatchers("/userlogin/") .antMatchers("/forgetPassword"); } 

Why Access-Control-Expose-Headers?

Access-Control-Expose-Headers (optional) - The XMLHttpRequest object has a getResponseHeader() method that returns the value of a particular response header. During a CORS request, the getResponseHeader() method can only access simple response headers. Simple response headers are defined as follows:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

If you want clients to be able to access other headers, you have to use the Access-Control-Expose-Headers header. The value of this header is a comma-delimited list of response headers you want to expose to the client.

response.setHeader("Access-Control-Expose-Headers", "Authorization"); 

AuthInterceptor

import { Injectable } from '@angular/core'; import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const token = window.localStorage.getItem('tokenKey'); // you probably want to store it in localStorage or something if (!token) { return next.handle(req); } const req1 = req.clone({ headers: req.headers.set('Authorization', `${token}`), }); return next.handle(req1); } } 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.