【问题标题】: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());
            }
        });
}

【问题讨论】:

    标签: java android spring jwt retrofit


    【解决方案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 并获取登录用户信息

    【讨论】:

      猜你喜欢
      • 2019-04-23
      • 2018-03-10
      • 2021-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-25
      • 2012-09-20
      • 2020-02-07
      相关资源
      最近更新 更多