title: Spring boot JWT auth by user id
body: <p>I am in trouble I want login by mail(username) and password but method getName on Principal object must return user Id not user mail. I am trying with userDetails but i don't have any correct result.</p>
  
  <p>I've got two type of users: Specialist and Customer which implemented UserDetails interface.</p>
  
  <pre><code>@Entity
  @Table(name = "customer")
  public class Customer implements UserDetails {
  
      @Id
      @GeneratedValue(generator = "uuid")
      @GenericGenerator(name = "uuid", strategy = "uuid2")
      @Column(name = "customer_id",updatable = false)
      private String customerId;
      @NotBlank(message = "Nickname may not be blank")
      @Size(min = 3, max = 20, message = "Nickname '${validatedValue}' isn't correct =&gt; must be between {min} and {max} characters")
      @Column(name = "nickname",updatable = false)
      private String nickname;
      @NotBlank(message = "City may not be blank")
      @Size(min = 3, max = 25, message = "City '${validatedValue}' isn't correct =&gt; must be between {min} and {max} characters")
      @Column(name = "city")
      private String city;
      @NotBlank(message = "Phone Number may not be blank")
      @Pattern(regexp="(^$|[0-9]{9})")
      @Column(name = "phone_number",updatable = false)
      private String phoneNumber;
      @NotBlank(message = "Email may not be blank")
      @Email
      @Column(name = "mail",updatable = false)
      private String mail;
      @Transient
      private String confirmMail;
      @NotBlank(message = "Password may not be blank")
      @Size(min = 5 , message = "Password '${validatedValue}' isn't correct =&gt; must be between {min} characters")
      @Column(name = "password")
      private String password;
      @Transient
      private String confirmPassword;
  </code></pre>
  
  <pre><code>
  @Entity
  @Table(name = "specialist")
  public class Specialist implements UserDetails {
      @Id
      @GeneratedValue(generator = "uuid")
      @GenericGenerator(name = "uuid", strategy = "uuid2")
      @Column(name = "specialist_id",updatable = false)
      private String specialistId;
      @NotBlank(message = "Name may not be blank")
      @Size(min = 3, max = 20, message = "Name '${validatedValue}' isn't correct =&gt; must be between {min} and {max} characters")
      @Column(name = "name",updatable = false)
      private String name;
      @NotBlank(message = "Surname may not be blank")
      @Size(min = 3, max = 20, message = "Name '${validatedValue}' isn't correct =&gt; must be between {min} and {max} characters")
      @Column(name = "surname",updatable = false)
      private String surname;
      @NotNull(message = "Province may not be blank")
      @Column(name = "province")
      private Province province;
      @NotBlank(message = "City may not be blank")
      @Size(min = 3, max = 25, message = "City '${validatedValue}' isn't correct =&gt; must be between {min} and {max} characters")
      @Column(name = "city")
      private String city;
      @NotBlank(message = "Profession may not be blank")
      @Size(min = 3, max = 25, message = "Profession '${validatedValue}' isn't correct =&gt; must be between {min} and {max} characters")
      @Column(name = "profession")
      private String profession;
      @NotBlank(message = "Phone Number may not be blank")
      @Pattern(regexp="(^$|[0-9]{9})")
      @Column(name = "phone_number",updatable = false)
      private String phoneNumber;
      @NotBlank(message = "Email may not be blank")
      @Email
      @Column(name = "mail",updatable = false)
      private String mail;
      @Transient
      private String confirmMail;
      @NotBlank(message = "Password may not be blank")
      @Size(min = 5 , message = "Password '${validatedValue}' isn't correct =&gt; must be between {min} characters")
      @Column(name = "password")
      private String password;
      @Transient
      private String confirmPassword;
      private Integer numberOfRatings = 0;
      @Range(min = 0 , max = 5)
      @Column(name = "average_rate")
      private Double averageRate = 0.0;
      private Double sumOfRatingsValue = 0.0;
  </code></pre>
  
  <p>Token Provider:</p>
  
  <pre><code>@Component
  public class JwtTokenProvider {
  //Start POINT
  public String generateTokenForSpecialist(Authentication authentication){
      Specialist specialist = (Specialist) authentication.getPrincipal();
      Date now = new Date(System.currentTimeMillis());
      Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);
      String userId = specialist.getSpecialistId();
      Map&lt;String,Object&gt; claims = new HashMap&lt;&gt;();
      claims.put("id",specialist.getSpecialistId());
      claims.put("mail", specialist.getMail());
  
  
      return Jwts.builder()
              .setSubject(userId)
              .setClaims(claims)
              .setIssuedAt(now)
              .setExpiration(expiryDate)
              .signWith(SignatureAlgorithm.HS512, SECRET)
              .compact();
  
  }
  
      public String generateTokenForCustomer(Authentication authentication){
          Customer customer = (Customer)authentication.getPrincipal();
          Date now = new Date(System.currentTimeMillis());
          Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);
          String userId = customer.getCustomerId();
          Map&lt;String,Object&gt; claims = new HashMap&lt;&gt;();
          claims.put("id",customer.getCustomerId());
          claims.put("mail", customer.getMail());
  
  
          return Jwts.builder()
                  .setSubject(userId)
                  .setClaims(claims)
                  .setIssuedAt(now)
                  .setExpiration(expiryDate)
                  .signWith(SignatureAlgorithm.HS512, SECRET)
                  .compact();
  
      }
  
  
      boolean validateToken(String token) {
          try{
              Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
              return true;
          }catch (SignatureException ex){
              System.out.println("Invalid JWT Signature");
          }catch (MalformedJwtException ex){
              System.out.println("Invalid JWT Token");
          }catch (ExpiredJwtException ex){
              System.out.println("Expired JWT token");
          }catch (UnsupportedJwtException ex){
              System.out.println("Unsupported JWT token");
          }catch (IllegalArgumentException ex){
              System.out.println("JWT claims string is empty");
          }
          return false;
      }
  
      String getUserIdFromJWT(String token){
          Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
          return (String)claims.get("id");
      }
  
  }
  </code></pre>
  
  <p>Filter:</p>
  
  <pre><code>public class JwtAuthenticationFilter extends OncePerRequestFilter {
  
      @Autowired
      private JwtTokenProvider tokenProvider;
  
      @Autowired
      private CustomUserDetailsService customUserDetailsService;
  
      @Override
      protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
  
          try{
  
              String jwt = getJWTFromRequest(httpServletRequest);
              if(StringUtils.hasText(jwt) &amp;&amp; tokenProvider.validateToken(jwt)){
                  String userId = tokenProvider.getUserIdFromJWT(jwt);
                  UserDetails user = customUserDetailsService.loadUserByUsername(userId);
  
                  UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                          user,null, user.getAuthorities()
                  );
  
                  authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                  SecurityContextHolder.getContext().setAuthentication(authentication);
              }
  
          }catch (Exception ex){
              logger.error("Could not set user authentication in security context", ex);
          }
  
          filterChain.doFilter(httpServletRequest,httpServletResponse);
      }
  
      private String getJWTFromRequest(HttpServletRequest request) {
  
          String bearerToken = request.getHeader(HEADER_STRING);
  
          if(StringUtils.hasText(bearerToken) &amp;&amp; bearerToken.startsWith(TOKEN_PREFIX)){
              return bearerToken.substring(7, bearerToken.length()-1);
          }
          return null;
  
      }
  }
  </code></pre>
  
  <pre><code>@Service
  public class CustomUserDetailsService implements UserDetailsService {
  
      private final SpecialistRepo specialistRepo;
  
      private final CustomerRepo customerRepo;
  
      @Autowired
      public CustomUserDetailsService(SpecialistRepo specialistRepo, CustomerRepo customerRepo) {
          this.specialistRepo = specialistRepo;
          this.customerRepo = customerRepo;
      }
  
      @Override
      public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
          if(specialistRepo.findByMail(username) != null){
              return specialistRepo.findByMail(username);
          }else if(customerRepo.findByMail(username) != null){
              return customerRepo.findByMail(username);
          }else throw new UsernameNotFoundException("User not found");
      }
  
      @Transactional
      public UserDetails loadUserById(String id){
          if(specialistRepo.findBySpecialistId(id) != null){
              return specialistRepo.findBySpecialistId(id);
          }else if(customerRepo.findByCustomerId(id) != null){
              return customerRepo.findByCustomerId(id);
          }else throw new  UsernameNotFoundException("User not found");
      }
  }
  </code></pre>
  
  <p>and login method in service:</p>
  
  <pre><code>  @Override
      public String login(LoginRequest loginRequest){
          Authentication authentication = authenticationManager.authenticate(
                  new UsernamePasswordAuthenticationToken(
                          loginRequest.getMail(),
                          loginRequest.getPassword()
                  )
          );
          SecurityContextHolder.getContext().setAuthentication(authentication);
          String jwtToken;
          if(customerRepo.findByMail(loginRequest.getMail()) == null){
              if(specialistRepo.findByMail(loginRequest.getMail()).isEnabled()) jwtToken = TOKEN_PREFIX + tokenProvider.generateTokenForSpecialist(authentication);
              else throw new UnconfirmedAccountException("Specialist Please confirm you account by token from mail");
          }else {
              if(customerRepo.findByMail(loginRequest.getMail()).isEnabled()) jwtToken = TOKEN_PREFIX + tokenProvider.generateTokenForCustomer(authentication);
              else throw new UnconfirmedAccountException("Customer Please confirm you account by token from mail");
          }
          return jwtToken;
      }
  </code></pre>
  
  <p>When I login successfully then i want for example to add commission from other service but in this way this work only if I find user by mail but i want find user by userId.</p>
  
  <p>There is a example:</p>
  
  <pre><code> @PostMapping("/add")
      public ResponseEntity&lt;?&gt; addCommission(@Valid @RequestBody Commission commission, Principal principal){
  
  
          Commission newCommission = commissionServiceImpl.createCommission(commission,principal.getName());
          return new ResponseEntity&lt;&gt;(newCommission,HttpStatus.CREATED);
      }
  </code></pre>
  
  <p>and service:</p>
  
  <pre><code>  @Override
      public Commission createCommission(Commission commission,String customerId) {
          try{
          Customer customer = customerRepo.findByCustomerId(customerId);
              commission.setCustomer(customer);
  
          return commissionRepo.save(commission);
          }catch (Exception e){
              throw new CommissionIdException("Commission with ID '" + commission.getCommissionId() + "' already exist";
          }
      }
  </code></pre>
  
  <p>Maybe anyone have got any idea to solve this because i must get Id by principal.getName. Otherwise I must rebuild all of my application.</p>