Spring with JWT auth,获取当前用户

0ve6wy6x  于 11个月前  发布在  Spring
关注(0)|答案(6)|浏览(141)

我有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

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

JWT AuthorizationFilter

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

qcbq4gxm

qcbq4gxm1#

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

private String getUserName() {
         JwtAuthenticationToken authenticationToken = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
    Jwt jwt = (Jwt) authenticationToken.getCredentials();
    String email = (String) jwt.getClaims().get("email");
    return email;
    }

字符串

n6lpvg4x

n6lpvg4x2#

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

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

}

字符串

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。它看起来像这样:

控制器

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

}


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

k5hmc34c

k5hmc34c4#

要检索自定义模型,我做以下事情:
从数据库中获取模型并将其设置为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;
    }

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

public ApplicationUser getCurrentUser(@AuthenticationPrincipal ApplicationUser user) {
    return user;
}

osh3o9ms

osh3o9ms5#

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

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

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

iih3973s

iih3973s6#

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

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

字符串

相关问题