Spring with JWT auth, get current user
Solution 1
In your case the @AuthenticationPrincipal
will return a string with the username,
you can get the user by calling the repository in your controller and getting the user by the username or declaring the repository as a @Bean
and do the folowing:
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
//Get the repository
private UserRepository userRepository;
public JWTAuthorizationFilter(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;
try {
user = Jwts.parser()
.setSigningKey(SECRET.getBytes())
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
} catch (SignatureException e) {
return null;
}
//Get your user
UserEntity userEntity = this.userRepository.findByUsername(user);
if (user != null) {
//Seting in your AuthenticationPrincipal the user
return new UsernamePasswordAuthenticationToken(userEntity, null, new ArrayList<>());
}
return null;
}
return null;
}
}
Solution 2
Check if you are using suitable annotation, because one of them is deprecated.
In addition, be aware to resolve username (String) as an argument, not User type:
Annotation that is used to resolve Authentication.getPrincipal() to a method argument.
Check this topic as well! It can help.
I don't know if it is good practice (I'm not considered 'pro' in Spring yet), but in my personal project I get token from HttpServletRequest object passed in controller parameter. Then I use JwtTokenUtil class, which have getUserFormToken(String token);
method to resolve user/username. It looks like this:
Controller
@Autowired
TestService testService;
@Autowired
UserService userService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@RequestMapping(value="/test", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public List<Test> getTestsListByUserId(HttpServletRequest req){
String token = req.getHeader(HEADER_STRING).replace(TOKEN_PREFIX,"");
return testService.findByUserId(userService.findByUsername(jwtTokenUtil.getUsernameFromToken(token)));
}
JwtTokenUtil
@Component
public class JwtTokenUtil implements Serializable {
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(SIGNING_KEY)
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
public String generateToken(User user) {
return doGenerateToken(user.getUsername());
}
private String doGenerateToken(String subject) {
Claims claims = Jwts.claims().setSubject(subject);
claims.put("scopes", Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")));
return Jwts.builder()
.setClaims(claims)
.setIssuer("issuer")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_VALIDITY_SECONDS*1000))
.signWith(SignatureAlgorithm.HS256, SIGNING_KEY)
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (
username.equals(userDetails.getUsername())
&& !isTokenExpired(token));
}
}
But I generally have different filters implementation according yo yours. If you are interested - I used this tutorial and implementation.
Solution 3
To retrieve a custom model i do next things:
Get model from Database and set it as Principal.
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user;
try {
user = Jwts.parser()
.setSigningKey(SECRET.getBytes())
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
} catch (SignatureException e) {
return null;
}
// Get user model
ApplicationUser userModel = userRepository.findByUsername(user);
// Set it
if (user != null && userModel != null) return new UsernamePasswordAuthenticationToken(userModel, null, new ArrayList<>());
return null;
}
return null;
}
Then in controller retrieve using @AuthenticationPrincipal
annotation.
public ApplicationUser getCurrentUser(@AuthenticationPrincipal ApplicationUser user) {
return user;
}
Solution 4
If this is still actual, I have just answered similar question here
Main point is to take authorization token
from header:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String token = request.getHeader("Authorization").split(" ")[1];
after that you can decode it and get parts which you need.
Related videos on Youtube
Alex
Updated on February 19, 2022Comments
-
Alex about 2 years
I have Spring Boot REST application which uses JWT tokens for authorization. I want to get current logged user in controllers using
@AuthenticationPrincipal
annotation. But it always returnsnull
if i return custom model fromloadUserByUsername
and auth stop working. My model implementsUserDetails
.I tried to extend the
org.springframework.security.core.userdetails.User
but i get rid errors from JWTAuthenticationFilter that default constructor not exists (ApplicationUser creds = new ObjectMapper().readValue(req.getInputStream(), ApplicationUser.class);
)Whats wrong?
UserDetailsServiceImpl.java
@Service public class UserDetailsServiceImpl implements UserDetailsService { private UserRepository userRepository; public UserDetailsServiceImpl(UserRepository userRepository) { this.userRepository = userRepository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { ApplicationUser applicationUser = userRepository.findByUsername(username); if (applicationUser == null) throw new UsernameNotFoundException(username); return applicationUser; } }
ApplicationUser.java (model)
@Entity @Table(name = "users") public class ApplicationUser implements UserDetails { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(unique = true, nullable = false) private String username; @Column(unique = true, nullable = false) private String email; @Column(nullable = false) private String password; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public boolean isAccountNonExpired() { return false; } @Override public boolean isAccountNonLocked() { return false; } @Override public boolean isCredentialsNonExpired() { return false; } @Override public boolean isEnabled() { return false; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; } }
JWTAuthenticationFilter
public class JWTAuthenticationFilter extends AbstractAuthenticationProcessingFilter { private AuthenticationManager authenticationManager; public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { super(new AntPathRequestMatcher(LOGIN_URL)); 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(((ApplicationUser) auth.getPrincipal()).getUsername()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET.getBytes()) .compact(); res.addHeader(HEADER_STRING, TOKEN_PREFIX + token); } }
JWTAuthorizationFilter
public class JWTAuthorizationFilter extends BasicAuthenticationFilter { public JWTAuthorizationFilter(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; try { user = Jwts.parser() .setSigningKey(SECRET.getBytes()) .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) .getBody() .getSubject(); } catch (SignatureException e) { return null; } if (user != null) return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>()); return null; } return null; } }