2
\$\begingroup\$

I developed a Spring RESTful service that uses a JWT for authorization. To the validity of this JWT, i used two different implementations.

  1. Create a filter to intercept every request and validate request, if invalid then reject it.
  2. Create a annotation to be used in controller methods, this approach is explained below.

The JWT contains user id which is used to identify authenticated user, but if i used a filter, i have to decode this JWT again in controller method, because of this problem i used second implementation.

The question is about annotation approach.

  • Is this a good implementation to JWT validation ?
  • How can i improve this code ?

Here's the code,

Annotation

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Authorize { String[] value(); } 

Implementation of the annotation

public class MethodAccessResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return (parameter.getMethodAnnotation(Authorize.class) != null) && (parameter.getParameterName().equals("jwt")); } @Override public Object resolveArgument( MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory ) throws Exception { String[] allowedUsers = parameter.getMethodAnnotation(Authorize.class).value(); JwtProperties jwt = JwtUtil.validate(webRequest.getHeader("Authorization")); String currentUser = jwt.getScope().get(0); int no = 0; for (String allowedUser : allowedUsers) { if (!allowedUser.equals(currentUser)) { no++; } } if (no == allowedUsers.length) { throw new UnauthorizedAccessException("Un-authorized access", 0xd1561); } else { return jwt; } } } 

Controller method - where the annotation is used

@GetMapping( path = "/api/event/post", produces = MediaType.APPLICATION_JSON_VALUE ) @Authorize(value = {UserSupport.USER, UserSupport.ADMIN, UserSupport.END_USER}) public ResponseEntity<?> getLiveLocations( HttpServletRequest request, JwtProperties jwt ) throws MongoException, ParseException, JwtException, UnsupportedEncodingException, ResourceNotFoundException, UnauthorizedAccessException { //do something } 

An Exception is thrown if token is invalid or the user role contains in token is not allowed to access particular URL.

If token is validated then user details will be injected to method parameter JwtProperties jwt.

\$\endgroup\$

    1 Answer 1

    1
    \$\begingroup\$
    • I'd prefer the filter, just because then you don't have to worry about a missed annotation leaving your API wide open. I never found decoding JWTs to be expensive, and I suspect you're performing premature optimization.

    • value isn't especially descriptive. Perhaps @Authorize(users= would be better.

    • Your code can be simplified by returning early if a match is found, rather than counting failures and comparing them:

      final JwtProperties jwt = JwtUtil.validate(webRequest.getHeader("Authorization")); final String currentUser = jwt.getScope().get(0); for (final String allowedUser : parameter.getMethodAnnotation(Authorize.class).value()) { if (allowedUser.equals(currentUser)) { return jwt; } } throw new UnauthorizedAccessException("Un-authorized access", 0xd1561); 
    \$\endgroup\$
    2
    • \$\begingroup\$appreciate your recommendations sir.\$\endgroup\$CommentedJul 6, 2018 at 16:26
    • \$\begingroup\$I use JWT with JWE, that's why i thought decoding could be an expensive task and has to process a lot of requests.\$\endgroup\$CommentedJul 6, 2018 at 16:38

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.