Project is divided on
----API Gateway (Zuul)
--- authentification services (Login/Signup , generete JWT Token)
--- CaclulateFees services
--- Calculaterate Services
My need is to generete token on authentification services with JWT ( that's Ok)
i need on each other services to validate token before execting the method.
i have add needed librairy to CalculateFees and then implements WebSecurityConfig extends WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// We don't need CSRF for this example
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/authenticate", "/register" ).permitAll().
anyRequest().authenticated().and().
// make sure we use stateless session; session won't be used to
// store user's state.
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
after that i implements CustomRequestFilter extends OncePerRequestFilter , to check Token and initiate a SecurityContext
Problem i keep getting 401 UNAUTHORIZED wherver i try to call the services and query not getting to customrequestfilter.
Please Help , i've try so many combinisation and configuration without succeeded.
I'm using SPRING BOOT 2.2.6.
below code for token validation
#Component
public class CustomRequestFilter extends OncePerRequestFilter{
#Autowired
private CustomJwtUserDetailsService jwtUserDetailsService;
#Autowired
private CustomJwtTokenProvider jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
Long userID = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get
// only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
userID = jwtTokenUtil.getUserIdFromJWT(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
// Once we get the token validate it.
if (userID != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserById(userID);
// if token is valid configure Spring Security to manually set
// authentication
if (jwtTokenUtil.validateToken(jwtToken)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
Related
I am attempting to follow this tutorial, which is a follow up to this tutorial, to implement JWT authorization for my API. When I attempt to use the '/authenticate' and '/register', I get the error "JWT Token does not begin with Bearer String".
Here is the part of the Request filter that throws the error:
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
Here is the websecurityconfig that should be allowing the 'authenticate' and 'request' requests through. I'm a bit confused as I cannot find where this is actually called in the tutorial:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// We don't need CSRF for this example
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/authenticate", "/register").permitAll().
// all other requests need to be authenticated
anyRequest().authenticated().and().
// make sure we use stateless session; session won't be used to
// store user's state.
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Attached is a link to the github.
https://github.com/Dikianify/ReporterAPI.git
Thanks
Because the jwtRequestFilter is a filter it will be called on every request (including /authenticate & /register), not just those that require authentication.
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
This is not a problem because the code is simply writing a warning to the logs and passing the request on down for further processing.
logger.warn("JWT Token does not begin with Bearer String");
When the user has been authenticated the Bearer header with the token should come through in each request for the filter to extract username.
I have this filter to read the token from the http header and set the security context :
public class AuthorizationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null) {
jwtToken = requestTokenHeader.substring(7);
try {
username = tokenService.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
log.error("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
log.error("JWT Token has expired");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
boolean isTokenValid = tokenService.validateToken(jwtToken, userDetails);
if (isTokenValid) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
Is there any default implementation that I can just config and use in spring boot security that reads the Authorization header and checks the token expiry date and then fill the needed info like user and roles in SecurityContext based on the token claims? since it seems like a very common functionality for user authorization , I thought maybe I didn't have to implement this part myself!
I cant get this cookie verification filter to work in springboot. I can also see the cookie in postman but not browser.
I generate the cookie in the following:
#PostMapping("/signin")
public ResponseEntity<?> authenticateUser(#RequestBody User loginRequest) {
Authentication authentication =
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getEmail(), loginRequest.getPassword())); // gets error here
SecurityContextHolder.getContext().setAuthentication(authentication);
MyUserDetails userDetails = (MyUserDetails) authentication.getPrincipal();
ResponseCookie jwtCookie = jwtUtils.generateJwtCookie(userDetails);
List<String> roles = userDetails.getAuthorities().stream().map(item -> item.getAuthority()).collect(Collectors.toList());
return ResponseEntity.ok().header(HttpHeaders.SET_COOKIE, jwtCookie.toString())
.body(userService.findUserProfileUserByEmail(userDetails.getEmail()));
}
When a request is sent to a restricted access endpoint, it will be run through this filter in the SecurityConfiguration
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
This is the class:
public class AuthTokenFilter extends OncePerRequestFilter {
#Autowired
private JwtUtils jwtUtils;
#Autowired
private MyUserDetailsService userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String email = jwtUtils.getEmailFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
System.out.println(SecurityContextHolder.getContext());
SecurityContextHolder.getContext().setAuthentication(authentication); // throws error here
System.out.println("a");
}
}
catch (Exception e) { logger.error("Cannot set user authentication: {}", e);
System.out.println(e);
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) { return jwtUtils.getJwtFromCookies(request); }
}
Here on line String jwt = parseJwt(request), it will always equal null.
I was told this may be an issue with the actual request itself, that it should contain {withCredentials: true} in Axios, though doing this raises other issues, and does not explain why this cookie exists and is visible in Postman.
I inherited a half-written Spring Boot REST service that is using Spring Sec to implement JWT-based API authentication. Gradle security-related dependencies are:
'org.springframework.security:spring-security-jwt:1.0.9.RELEASE'
'org.springframework.security.oauth:spring-security-oauth2:2.2.1.RELEASE'
'io.jsonwebtoken:jjwt:0.9.0'
'org.springframework.boot:spring-boot-starter-security'
This app uses Spring Sec filters to implement the entire auth solution, and I'm trying to wrap my head around how it works, and for the life of me can't make sense of a few critical things :-/
Here's the code:
public class MyAppAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public MyAppAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
ApplicationUser creds = new ObjectMapper()
.readValue(req.getInputStream(), ApplicationUser.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String token = Jwts.builder()
.setSubject(((User) auth.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
.compact();
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
public class MyAppAuthorizationFilter extends BasicAuthenticationFilter {
public MyAppAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = Jwts.parser()
.setSigningKey(SECRET.getBytes())
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
#Component
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private AccountDAO accountDAO;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountDAO.findByUsername(username);
if(account == null) {
throw new UsernameNotFoundException(username);
}
return new User(account.username, account.password, []);
}
}
What I'm not understanding is:
Can I assume that Spring Security automagically positions these filters in the correct sequence? That is: the MyAppAuthenticationFilter always gets called before the MyAppAuthorizationFilter?
I'm really confused by the authenticationManager.authenticate(...) call inside MyAppAuthenticationFilter#attemptAuthentication. How are creds.getUsename() and cred.getPassword() compared to user information stored in a database (or LDAP or anywhere else)? How does this mechanism relate to UserDetailsServiceImpl#loadByUsername(String)?
All of the logic in MyAppAuthorizationFilter#doFilterInternal doesn't make sense to me. To me, I read it as: check to see if there is a JWT token header on the request. If there isn't, then go ahead and make the request any way (!!!!). If there is, then go ahead and check that the JWT has a valid user as its subject. Shouldn't we be blocking the request if there's no JWT header on the request?
I have a spring data rest micro-service, with following entities
User
Laptop
Specs
User has list of laptops, and each one has it's own specs. Now the point is, user can log in through another micro-service, and get a JWT. Then, he creates a laptop for himself, and adds tech specs to the laptop.
Here's the call for adding the specs to the laptop
curl -i -X PUT http://localhost:8080/laptop/1/specs -H "Content-Type:text/uri-list" -d "http://localhost:8080/specs/1"
I can get my user's id from the JWT in my security filter, so how can I validate that the resource "belongs" to the user? How do I make sure that the user doesn't update other user's laptop specs? This is just the showcase, in fact number of models is around 30, so is there a dynamic solution to verify the user/resource association?
Here's the code of my security filter
public class JWTAuthenticationFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
Authentication authentication = JWTAuthenticationService.getAuthentication((HttpServletRequest) request);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
JWT validator
public static Authentication getAuthentication(HttpServletRequest request) {
String authHeader = request.getHeader(AUTHORIZATION_HEADER);
String authToken = authHeader != null ? authHeader.replace(TOKEN_PREFIX, "") : null;
if (authToken != null) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(authToken)
.getBody();
String username = claims.getSubject();
#SuppressWarnings("unchecked") final List<String> authoritiesClaim = (List<String>) claims.get(AUTHORITIES);
final List<SimpleGrantedAuthority> authorities = authoritiesClaim
.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return username != null ?
new UsernamePasswordAuthenticationToken(username, null, authorities) :
null;
}
return null;
}
and web security configuration
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(new JWTAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}