Spring with JWT auth,获取当前用户

0ve6wy6x  于 2024-01-05  发布在  Spring
关注(0)|答案(6)|浏览(180)

我有Sping Boot REST应用程序,它使用JWT令牌进行授权。我想使用@AuthenticationPrincipal注解在控制器中获取当前登录用户。但如果我从loadUserByUsername返回自定义模型,它总是返回null,并且auth停止工作。我的模型实现了UserDetails
我试图扩展org.springframework.security.core.userdetails.User,但我从JWTAuthenticationFilter中删除了默认构造函数不存在的错误(ApplicationUser creds = new ObjectMapper().readValue(req.getInputStream(), ApplicationUser.class);
怎么了?

UserDetailsServiceImpl.java

  1. @Service
  2. public class UserDetailsServiceImpl implements UserDetailsService {
  3. private UserRepository userRepository;
  4. public UserDetailsServiceImpl(UserRepository userRepository) {
  5. this.userRepository = userRepository;
  6. }
  7. @Override
  8. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  9. ApplicationUser applicationUser = userRepository.findByUsername(username);
  10. if (applicationUser == null) throw new UsernameNotFoundException(username);
  11. return applicationUser;
  12. }
  13. }

字符串

ApplicationUser.java(model)

  1. @Entity
  2. @Table(name = "users")
  3. public class ApplicationUser implements UserDetails {
  4. private static final long serialVersionUID = 1L;
  5. @Id
  6. @GeneratedValue(strategy = GenerationType.IDENTITY)
  7. private long id;
  8. @Column(unique = true, nullable = false)
  9. private String username;
  10. @Column(unique = true, nullable = false)
  11. private String email;
  12. @Column(nullable = false)
  13. private String password;
  14. public long getId() {
  15. return id;
  16. }
  17. public void setId(long id) {
  18. this.id = id;
  19. }
  20. public String getUsername() {
  21. return username;
  22. }
  23. public void setUsername(String username) {
  24. this.username = username;
  25. }
  26. public String getEmail() {
  27. return email;
  28. }
  29. public void setEmail(String email) {
  30. this.email = email;
  31. }
  32. public String getPassword() {
  33. return password;
  34. }
  35. public void setPassword(String password) {
  36. this.password = password;
  37. }
  38. @Override
  39. public boolean isAccountNonExpired() {
  40. return false;
  41. }
  42. @Override
  43. public boolean isAccountNonLocked() {
  44. return false;
  45. }
  46. @Override
  47. public boolean isCredentialsNonExpired() {
  48. return false;
  49. }
  50. @Override
  51. public boolean isEnabled() {
  52. return false;
  53. }
  54. @Override
  55. public Collection<? extends GrantedAuthority> getAuthorities() {
  56. return null;
  57. }
  58. }

JWTAuthenticationFilter

  1. public class JWTAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
  2. private AuthenticationManager authenticationManager;
  3. public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
  4. super(new AntPathRequestMatcher(LOGIN_URL));
  5. this.authenticationManager = authenticationManager;
  6. }
  7. @Override
  8. public Authentication attemptAuthentication(HttpServletRequest req,
  9. HttpServletResponse res) throws AuthenticationException {
  10. try {
  11. ApplicationUser creds = new ObjectMapper()
  12. .readValue(req.getInputStream(), ApplicationUser.class);
  13. return authenticationManager.authenticate(
  14. new UsernamePasswordAuthenticationToken(
  15. creds.getUsername(),
  16. creds.getPassword(),
  17. new ArrayList<>())
  18. );
  19. } catch (IOException e) {
  20. throw new RuntimeException(e);
  21. }
  22. }
  23. @Override
  24. protected void successfulAuthentication(HttpServletRequest req,
  25. HttpServletResponse res,
  26. FilterChain chain,
  27. Authentication auth) throws IOException, ServletException {
  28. String token = Jwts.builder()
  29. .setSubject(((ApplicationUser) auth.getPrincipal()).getUsername())
  30. .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
  31. .signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
  32. .compact();
  33. res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
  34. }
  35. }

JWT AuthorizationFilter

  1. public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
  2. public JWTAuthorizationFilter(AuthenticationManager authManager) {
  3. super(authManager);
  4. }
  5. @Override
  6. protected void doFilterInternal(HttpServletRequest req,
  7. HttpServletResponse res,
  8. FilterChain chain) throws IOException, ServletException {
  9. String header = req.getHeader(HEADER_STRING);
  10. if (header == null || !header.startsWith(TOKEN_PREFIX)) {
  11. chain.doFilter(req, res);
  12. return;
  13. }
  14. UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
  15. SecurityContextHolder.getContext().setAuthentication(authentication);
  16. chain.doFilter(req, res);
  17. }
  18. private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
  19. String token = request.getHeader(HEADER_STRING);
  20. if (token != null) {
  21. // parse the token.
  22. String user;
  23. try {
  24. user = Jwts.parser()
  25. .setSigningKey(SECRET.getBytes())
  26. .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
  27. .getBody()
  28. .getSubject();
  29. } catch (SignatureException e) {
  30. return null;
  31. }
  32. if (user != null) return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
  33. return null;
  34. }
  35. return null;
  36. }
  37. }

qcbq4gxm

qcbq4gxm1#

我最近实现了一种方法,可以在SpringBoot中从JWT token获取用户名或电子邮件。

  1. private String getUserName() {
  2. JwtAuthenticationToken authenticationToken = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
  3. Jwt jwt = (Jwt) authenticationToken.getCredentials();
  4. String email = (String) jwt.getClaims().get("email");
  5. return email;
  6. }

字符串

n6lpvg4x

n6lpvg4x2#

在您的情况下,@AuthenticationPrincipal将返回一个带有用户名的字符串,您可以通过在控制器中调用存储库并通过用户名获取用户,或者将存储库声明为@Bean并执行以下操作来获取用户:

  1. public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
  2. //Get the repository
  3. private UserRepository userRepository;
  4. public JWTAuthorizationFilter(AuthenticationManager authManager) {
  5. super(authManager);
  6. }
  7. @Override
  8. protected void doFilterInternal(HttpServletRequest req,
  9. HttpServletResponse res,
  10. FilterChain chain) throws IOException, ServletException {
  11. String header = req.getHeader(HEADER_STRING);
  12. if (header == null || !header.startsWith(TOKEN_PREFIX)) {
  13. chain.doFilter(req, res);
  14. return;
  15. }
  16. UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
  17. SecurityContextHolder.getContext().setAuthentication(authentication);
  18. chain.doFilter(req, res);
  19. }
  20. private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
  21. String token = request.getHeader(HEADER_STRING);
  22. if (token != null) {
  23. // parse the token.
  24. String user;
  25. try {
  26. user = Jwts.parser()
  27. .setSigningKey(SECRET.getBytes())
  28. .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
  29. .getBody()
  30. .getSubject();
  31. } catch (SignatureException e) {
  32. return null;
  33. }
  34. //Get your user
  35. UserEntity userEntity = this.userRepository.findByUsername(user);
  36. if (user != null) {
  37. //Seting in your AuthenticationPrincipal the user
  38. return new UsernamePasswordAuthenticationToken(userEntity, null, new ArrayList<>());
  39. }
  40. return null;
  41. }
  42. return null;
  43. }
  44. }

字符串

展开查看全部
2eafrhcq

2eafrhcq3#

检查是否使用了合适的注解,因为其中一个注解已被弃用。
Documentation - deprecated!
Documentation - fine!
此外,请注意将username(String)解析为参数,而不是User type:
用于将Authentication.getPrincipal()解析为方法参数的注解。
Check this topic as well!它可以帮助。
我不知道这是否是一个好的做法(我还没有被认为是Spring的“专业人士”),但在我的个人项目中,我从传递给控制器参数的HttpServletRequest对象中获取token。然后我使用JwtTokenUtil类,它有getUserFormToken(String token);方法来解析user/username。它看起来像这样:

控制器

  1. @Autowired
  2. TestService testService;
  3. @Autowired
  4. UserService userService;
  5. @Autowired
  6. private JwtTokenUtil jwtTokenUtil;
  7. @RequestMapping(value="/test", method = RequestMethod.GET, produces = "application/json")
  8. @ResponseBody
  9. public List<Test> getTestsListByUserId(HttpServletRequest req){
  10. String token = req.getHeader(HEADER_STRING).replace(TOKEN_PREFIX,"");
  11. return testService.findByUserId(userService.findByUsername(jwtTokenUtil.getUsernameFromToken(token)));
  12. }

字符串

JwtTokenUtil

  1. @Component
  2. public class JwtTokenUtil implements Serializable {
  3. public String getUsernameFromToken(String token) {
  4. return getClaimFromToken(token, Claims::getSubject);
  5. }
  6. public Date getExpirationDateFromToken(String token) {
  7. return getClaimFromToken(token, Claims::getExpiration);
  8. }
  9. public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
  10. final Claims claims = getAllClaimsFromToken(token);
  11. return claimsResolver.apply(claims);
  12. }
  13. private Claims getAllClaimsFromToken(String token) {
  14. return Jwts.parser()
  15. .setSigningKey(SIGNING_KEY)
  16. .parseClaimsJws(token)
  17. .getBody();
  18. }
  19. private Boolean isTokenExpired(String token) {
  20. final Date expiration = getExpirationDateFromToken(token);
  21. return expiration.before(new Date());
  22. }
  23. public String generateToken(User user) {
  24. return doGenerateToken(user.getUsername());
  25. }
  26. private String doGenerateToken(String subject) {
  27. Claims claims = Jwts.claims().setSubject(subject);
  28. claims.put("scopes", Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")));
  29. return Jwts.builder()
  30. .setClaims(claims)
  31. .setIssuer("issuer")
  32. .setIssuedAt(new Date(System.currentTimeMillis()))
  33. .setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_VALIDITY_SECONDS*1000))
  34. .signWith(SignatureAlgorithm.HS256, SIGNING_KEY)
  35. .compact();
  36. }
  37. public Boolean validateToken(String token, UserDetails userDetails) {
  38. final String username = getUsernameFromToken(token);
  39. return (
  40. username.equals(userDetails.getUsername())
  41. && !isTokenExpired(token));
  42. }
  43. }


但我通常有不同的过滤器实现根据你的。如果你有兴趣-我用this教程和实现。

展开查看全部
k5hmc34c

k5hmc34c4#

要检索自定义模型,我做以下事情:
从数据库中获取模型并将其设置为Principal。

  1. private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
  2. String token = request.getHeader(HEADER_STRING);
  3. if (token != null) {
  4. // parse the token.
  5. String user;
  6. try {
  7. user = Jwts.parser()
  8. .setSigningKey(SECRET.getBytes())
  9. .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
  10. .getBody()
  11. .getSubject();
  12. } catch (SignatureException e) {
  13. return null;
  14. }
  15. // Get user model
  16. ApplicationUser userModel = userRepository.findByUsername(user);
  17. // Set it
  18. if (user != null && userModel != null) return new UsernamePasswordAuthenticationToken(userModel, null, new ArrayList<>());
  19. return null;
  20. }
  21. return null;
  22. }

字符串
然后在控制器中使用@AuthenticationPrincipal注解检索。

  1. public ApplicationUser getCurrentUser(@AuthenticationPrincipal ApplicationUser user) {
  2. return user;
  3. }

展开查看全部
osh3o9ms

osh3o9ms5#

如果这仍然是实际的,我刚才回答了类似的问题here
要点是从header中取出authorization token

  1. HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
  2. String token = request.getHeader("Authorization").split(" ")[1];

字符串
然后你就可以解码得到你需要的零件。

iih3973s

iih3973s6#

现在你可以做到这一点,它很容易和更简单:

  1. public User getCurrentUser() {
  2. Authentication authenticationToken = SecurityContextHolder.getContext().getAuthentication();
  3. return (User)authenticationToken.getPrincipal();
  4. }

字符串

相关问题