我有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;
}
}
型
6条答案
按热度按时间qcbq4gxm1#
我最近实现了一种方法,可以在SpringBoot中从JWT token获取用户名或电子邮件。
字符串
n6lpvg4x2#
在您的情况下,
@AuthenticationPrincipal
将返回一个带有用户名的字符串,您可以通过在控制器中调用存储库并通过用户名获取用户,或者将存储库声明为@Bean
并执行以下操作来获取用户:字符串
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。它看起来像这样:控制器
字符串
JwtTokenUtil
型
但我通常有不同的过滤器实现根据你的。如果你有兴趣-我用this教程和实现。
k5hmc34c4#
要检索自定义模型,我做以下事情:
从数据库中获取模型并将其设置为Principal。
字符串
然后在控制器中使用
@AuthenticationPrincipal
注解检索。型
osh3o9ms5#
如果这仍然是实际的,我刚才回答了类似的问题here
要点是从header中取出
authorization token
:字符串
然后你就可以解码得到你需要的零件。
iih3973s6#
现在你可以做到这一点,它很容易和更简单:
字符串