有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

但我不确定这样发展是否正确

假设我的数据库中有两个表

  • 一个具有用于身份验证的登录凭据
  • 其次是用户个人数据

enter image description here

现在我想显示一个用户的名字和姓氏

现在,我登录后唯一的信息是他登录时使用的用户名,如果我想获得更多信息,我必须在客户端查询:

  • 首先-获取用户名=我从令牌获得的用户名的用户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. # 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,并登录用户信息