java使用JWT身份验证与用户合作的正确方式是什么?
我已经用REST API和JWT身份验证/授权为我的移动应用程序创建了后端
然后,我使用改造创建了安卓应用程序
从/login端点检索JWToken后,我在服务器端创建了GET请求,用令牌解析当前登录用户的用户名,然后调用客户端的方法
用户控制器。java(服务器端)
@RestController
@RequestMapping(path = "/user")
public class UserController {
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public String getCurrentUser(@AuthenticationPrincipal Object user) {
user = SecurityContextHolder.getContext().getAuthentication()
.getPrincipal();
return user.toString();
}
}
但我不确定这样发展是否正确
假设我的数据库中有两个表
- 一个具有用于身份验证的登录凭据
- 其次是用户个人数据
现在我想显示一个用户的名字和姓氏
现在,我登录后唯一的信息是他登录时使用的用户名,如果我想获得更多信息,我必须在客户端查询:
- 首先-获取用户名=我从令牌获得的用户名的用户id
- 然后-从第一个查询中获取users_data的对象,其中user_id=id
我认为这个过程不应该在客户端完成(如果我错了,请纠正我)
问题:
所以我的问题是,我应该做些什么来实现这个场景,在客户端应用程序中,我想获得关于用户的所有信息,而我只有他的用户名。我应该在后端进行更改,还是坚持使用移动应用程序进行查询
(服务器端)
AuthenticationFilter。java
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(
HttpServletRequest request,
HttpServletResponse response
) throws AuthenticationException {
// Mapping credentials to loginviewmodel
LoginViewModel credentials = null;
try {
credentials = new ObjectMapper().readValue(request.getInputStream(), LoginViewModel.class);
} catch (IOException e) {
e.printStackTrace();
}
// Creating login token
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
credentials.getUsername(),
credentials.getPassword(),
new ArrayList<>()
);
// Authenticate user
Authentication auth = authenticationManager.authenticate(authenticationToken);
return auth;
}
@Override
protected void successfulAuthentication(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult
) throws IOException, ServletException {
// Grab current user
UserImpl principal = (UserImpl) authResult.getPrincipal();
// Create JWT Token
String token = JWT.create()
.withSubject(principal.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + JwtProperties.EXPIRATION_TIME))
.sign(Algorithm.HMAC512(JwtProperties.SECRET.getBytes()));
// Add token in response(this is syntax of token)
response.addHeader(JwtProperties.HEADER_STRING, JwtProperties.TOKEN_PREFIX + token);
}
}
授权过滤器。java
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private UserRepository userRepository;
public JwtAuthorizationFilter(
AuthenticationManager authenticationManager,
UserRepository userRepository
) {
super(authenticationManager);
this.userRepository = userRepository;
}
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain
) throws IOException, ServletException {
// Read authorization header with JWT Token
String header = request.getHeader(JwtProperties.HEADER_STRING);
if (header == null || !header.startsWith(JwtProperties.TOKEN_PREFIX)) {
chain.doFilter(request, response);
}
// Try get user data from DB to authorize
Authentication authentication = getUsernamePasswordAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
private Authentication getUsernamePasswordAuthentication(HttpServletRequest request) {
String token = request.getHeader(JwtProperties.HEADER_STRING);
if (token != null) {
// parse and validate token
String username = JWT.require(Algorithm.HMAC512(JwtProperties.SECRET.getBytes()))
.build()
.verify(token.replace(JwtProperties.TOKEN_PREFIX, ""))
.getSubject();
if (username != null) {
User user = userRepository.findByUsername(username);
UserImpl principal = new UserImpl(user);
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null, principal.getAuthorities());
return auth;
}
return null;
}
return null;
}
}
UserImpl。java
public class UserImpl implements UserDetails {
private User user;
public UserImpl(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
// Get list of roles (ROLE_name)
this.user.getRoleList().forEach( role -> {
GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + role);
authorities.add(authority);
});
return authorities;
}
@Override
public String getPassword() {
return this.user.getPassword();
}
@Override
public String getUsername() {
return this.user.getUsername();
}
}
(客户端)
解析当前登录用户的用户名的方法:
public void getCurrentUser() {
Call<String> call = ApiClient.getUserService(getApplicationContext()).getCurrentUser();
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
String user = response.body();
nameOfUserView.setText(user);
}
}
@Override
public void onFailure(Call<String> call, Throwable t) {
nameOfUserView.setText(t.getMessage());
}
});
}
# 1 楼答案
你的逻辑有漏洞。让我们看看
#1-很好。基于JWT令牌,您正在获取用户名 #2-使用头中的令牌,通过发送用户名获取其他详细信息
在#2中,如果我发送任何其他用户的用户名,而不是登录用户的用户名,该怎么办。系统仍将给出详细信息。因此,任何登录的用户都可以看到任何用户的详细信息
要处理这个问题,您应该使用一些DTO类,它包含所有必需的字段,比如UserReturnData。结构将是
然后在当前用户呼叫中,根据授权标头填充此数据。不要发送任何用户名。授权标头应足以获取用户详细信息。样本:
无论何时需要登录用户的详细信息。您可以仅使用授权令牌调用当前用户API,并登录用户信息