JWT Guest Authentication with Spring - spring

I am looking this project (https://github.com/szerhusenBC/jwt-spring-security-demo) to understand how to work the JWT Authentication. I want to implement the Guest Authentication with and without credentials (these users aren't in DB). I have modified the class JwtAuthenticationFilter to support the AnonymousAuthentication, but if I don't send the body in the Request I have the bad request error. I want modify this project to obtain the token without send username and password credentials to server. Is it possibile to do ? Or can I make a guest account ? Can I have a hint ?
...
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse resp,
FilterChain chain)
throws ServletException, IOException {
String token = req.getHeader(this.tokenHeader);
String username = tokenUtil.getUsernameFromToken(token);
if(username != null && SecurityContextHolder.getContext().getAuthentication() == null)
{
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (tokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(req));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
else
{
AnonymousAuthenticationToken authguest = new AnonymousAuthenticationToken("anonymous","anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
authguest.setDetails(new WebAuthenticationDetailsSource().buildDetails(req));
SecurityContextHolder.getContext().setAuthentication(authguest);
}
chain.doFilter(req, resp);
}
...

Related

automatically fill SecurityContext from Authorization header from Spring security authorization

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!

Getting data from security token in rest controller

I am working with Spring MVC and Spring Security for rest controllers, also I am using JWT. In some cases, I need to get a username from the token to provide it as a function parameter.
Now I am solving this problem by setting username as a request attribute in the security filter.
Can you advise me on a better way to do this?
Rest controller:
#GetMapping(path = "/user", produces = "application/json")
public String getUserFromToken(#RequestAttribute(name = "username") String username) throws JsonProcessingException {
List<Data> dataSet = userService.doSomeProcessing(username);
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(dataSet);
}
Security filter:
#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 && 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");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
request.setAttribute("username", jwtTokenUtil.getUsernameFromToken(jwtToken));
}
}
chain.doFilter(request, response);
}
You should read about principal
https://www.baeldung.com/get-user-in-spring-security
There you will find a solution to this Spring Security problem, you yourself met with a similar one, thanks for the question and good luck

Spring security BasicAuthenticationFilter returns 403 instead of 401

I have implemented the JWT authentication and authorization. Everything is working fine, besides the unauthorized scenario
Unauthorized scenario: making a http call to a route without providing a authorization token.
Result: 403 forbidden instead of unauthorized
Here is my code:
#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);
}
After the
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
Executes, the response is 403
Here is my full class:
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
#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) {
// setting the user in the security context
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
if(user != null){
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
Remark:
I had the same problem with UsernamePasswordAuthenticationFilter, and I solved it by overriding the default authenticationFailureHandler:
setAuthenticationFailureHandler(new JWTAuthenticationFailureHandler());
How can I get the correct 401 response code and body?
Thanks!
If you look at what BasicAuthenticationFilter which you are overriding with JWTAuthorizationFilter does when authentication fails, it calls authenticationEntryPoint.commence(request, response, failed) which sends 401
BasicAuthenticationEntryPoint
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.addHeader("WWW-Authenticate", "Basic realm=\"" + realmName + "\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
}
But you have behaviour that overridden and returning null. So instead of that try one of the following:
Throw BadCredentialsException where you are returning null
Do response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());

problem with Spring Security with microservices

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);
}
}

How to secure spring boot data rest security based on logged in user?

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);
}

Resources