使用 JWT 身份验证与用户合作的正确方法是啥?
Posted
技术标签:
【中文标题】使用 JWT 身份验证与用户合作的正确方法是啥?【英文标题】:What's a correct way to work with users using JWT authentication?使用 JWT 身份验证与用户合作的正确方法是什么? 【发布时间】:2021-07-30 02:05:23 【问题描述】:我使用 REST API 和 JWT 身份验证/授权为我的移动应用程序创建了后端。 然后我使用 Retrofit 创建了 android 应用程序。 从 /login 端点检索 JWToken 后,我在服务器端创建了 GET 请求,以使用令牌解析当前登录用户的用户名,然后在客户端调用方法。
UserController.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,其中 username = 我从令牌中获得的用户名 然后 - 从第一个查询中获取 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);
AuthorizationFilter.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。结构将是
public class UserReturnData
String username;
List<String> roles;
Long id;
//more fields as per requirement
然后在您当前的用户调用中,根据授权标头填充此数据。不要发送任何用户名。授权标头应该足以获取用户详细信息。示例:
public UserReturnData fetchUserDetails()
UserReturnData userReturnData = new UserReturnData();
List<String> roles = new ArrayList<String>();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
userReturnData.setUsername(auth.getName());
Long id = uRepo.findId(auth.getName());
userReturnData.setId(id);
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder
.getContext().getAuthentication().getAuthorities();
for (SimpleGrantedAuthority authority : authorities)
roles.add(authority.getAuthority());
userReturnData.setRoles(roles);
//Populate other required fields here.
return userReturnData;
当您需要登录用户的详细信息时。您可以仅使用授权令牌调用当前用户 API 并获取登录用户信息
【讨论】:
以上是关于使用 JWT 身份验证与用户合作的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章