【问题标题】:How can I get the username from the JWT?如何从 JWT 获取用户名?
【发布时间】:2020-10-26 14:32:06
【问题描述】:

我有 2 个微服务。向客户发送消息的 API 网关和微服务。 我的 API 在身份验证后返回一个 JWT 令牌。用户的用户名包含在令牌中。

        public String extractUsername(String token)
        {
            return extractClaim(token, Claims::getSubject);
        }
        public Date extractExpiration(String token)
        {
            return extractClaim(token, Claims::getExpiration);
        }
        public <T> T extractClaim(String token, Function<Claims,T> claimsResolver)
        {
            final Claims claims = extractAllClaims(token);
            return claimsResolver.apply(claims);
        }
        private Claims extractAllClaims(String token)
        {
            return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
        }
        private Boolean isTokenExpired(String token)
        {
            return extractExpiration(token).before(new Date());
        }
        public String generateToken(UserDetails userDetails)
        {
            Map<String,Object> claims =new HashMap<>();
            return createToken(claims,userDetails.getUsername());
        }
        private String createToken(Map<String, Object> claims, String subject)
        {
            return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
                    .setExpiration(new Date(System.currentTimeMillis() + 1000*60*60*24*360))
                    .signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
        }
        public Boolean validateToken(String token, UserDetails userDetails)
        {
            final String username = extractUsername(token);
            return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
        }

如果我想通过 API 网关寻址我的 SendMessage 微服务,他需要用户名来发送消息。我目前正在通过 Rest-Api 传递用户名。我希望 SendMessage 微服务从令牌中获取用户名。我读过这适用于 TokenEnhancer。但我没有找到更多信息。谁能告诉我如何做到这一点或在哪里可以找到更多信息?

更新

@GetMapping("/contacts/sms/{name}/{customer_phone_number}/{text}")
            public String sendSmsToCustomer(@PathVariable("name") String name,@PathVariable("customer_phone_number") String customer_phone_number, @PathVariable("text") String text) throws Exception
            {
            
String user= getUsernameFromToken(HttpServletRequest request);
            
            
            SmsSubmissionResponse responses = client.getSmsClient().submitMessage(new TextMessage(
                    name,
                    customer_phone_number,
                    text));
            for (SmsSubmissionResponseMessage response : responses.getMessages()) {
                System.out.println (response);
            }
        
            if (responses.getMessages().get(0).getStatus() == MessageStatus.OK) {
                String date=new SimpleDateFormat("yyyy.MM.dd - HH:mm:ss").format(new java.util.Date());
                    SQL_Connection.SaveDataInSmsDB(name, customer_phone_number, text,date);
                    return "Message sent successfully.";
                } else {
                    return "Message failed with error: " + responses.getMessages().get(0).getErrorText();
                }
            }

更新 2

@GetMapping("/contacts/sms/{name}/{customer_phone_number}/{text}")
            public String sendSmsToCustomer(@RequestHeader(HEADER_STRING) HttpServletRequest request, @PathVariable("name") String name,@PathVariable("customer_phone_number") String customer_phone_number, @PathVariable("text") String text) throws Exception
            {
            String user= getUsernameFromToken(request);
            System.out.println(user);
{
    "timestamp": "2020-07-07T08:08:34.975+0000",
    "status": 400,
    "error": "Bad Request",
    "message": "Missing request header 'Authorization' for method parameter of type HttpServletRequest",
    "path": "/contacts/sms/segos/44256674/hallo%20Container%20kommt"
}

【问题讨论】:

    标签: java spring-boot jwt


    【解决方案1】:

    您可以使用以下代码从 Token 中提取用户

     String token = request.getHeader(HEADER_STRING);
        String user = null;
        if (token != null) {
            // parse the token.
    
            try {
                user = Jwts.parser()
                        .setSigningKey(SECRET)
                        .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                        .getBody()
                        .getSubject();
    
    
            } catch (Exception e) {
    
                throw e;
    
            }
    

    您可以参考完整的代码和项目

    https://github.com/techiesantosh/taskmanager-service/blob/develop/src/main/java/com/web/taskmanager/auth/TokenAuthenticationService.java

    【讨论】:

    • 好吧听起来合乎逻辑。但是如何在我的 RestController 中调用该函数,以便我可以从 REST API 中删除 {name} ?我更新了我当前的解决方案
    • 根据标准,JWT 是您的标头的一部分。调用 sendMessage 控制器时将 JWT 传入标头。在 sendSmsToCustomer 方法中添加 HttpServletRequest 作为参数,您应该能够从请求标头访问 JWT 令牌
    • 现在我收到了错误的请求状态 400。我做错了什么?我再次更新了我的代码。非常感谢您的帮助
    【解决方案2】:

    通过authentication.getName & principal.getName 你可以获得用户名:

    试试下面的代码:

    @Controller
    @RequestMapping("/info")
    public class GetNameController {
    
        @RequestMapping(value = "/name", method = RequestMethod.GET)
        public String getName(Authentication authentication, Principal principal) {
            System.out.println(authentication.getName());
            System.out.println(principal.getName());
            return "";
        }
    }
    

    【讨论】:

      【解决方案3】:

      Authenticate 后可以通过 Overriding successfulAuthentication() 方法发送 JWT Token

      生成令牌代码:

      @Override
      protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
              Authentication authResult) throws IOException, ServletException {
          String userName = ((User)authResult.getPrincipal()).getUsername();
          UserDto userDetails = userService.getUserDetailsByEmail(userName);
          
          // Generate JWO Token
          String token = Jwts.builder()
                  .setSubject(userDetails.getUserId())
                  .setExpiration(new Date(System.currentTimeMillis() + Long.parseLong(env.getProperty("token.expiration_time"))))
                  .signWith(SignatureAlgorithm.HS512, env.getProperty("token.secret"))
                  .compact();
          response.addHeader("token", token);
          response.addHeader("userId", userDetails.getUserId());
      }
      

      SecurityConfig.java

      @Configuration
      @EnableWebSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter{
          @Autowired
          private Environment env;
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              
              http.csrf().disable();
              http.headers().frameOptions().disable();
              http.authorizeRequests()
              .antMatchers(env.getProperty("api.h2console.url.path")).permitAll()
              .antMatchers(HttpMethod.POST, env.getProperty("api.registration.url.path")).permitAll()
              .antMatchers(HttpMethod.POST, env.getProperty("api.login.url.path")).permitAll()
              .anyRequest().authenticated();
              
              http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
          }   
      }
      

      获取请求后,将使用http.anyRequest().authenticated() 方法进行身份验证。 然后它会调用跟随授权过滤器对请求进行身份验证。

      AuthorizationFilter.java

      public class AuthorizationFilter extends BasicAuthenticationFilter{
      
          Environment environment;
          
          public AuthorizationFilter(AuthenticationManager authenticationManager, Environment environment) {
              super(authenticationManager);
              this.environment = environment;
          }
      
          @Override
          protected void doFilterInternal(HttpServletRequest req,HttpServletResponse res, FilterChain filter)
              throws IOException, ServletException{
              
              String authorizationHeader = req.getHeader(environment.getProperty("authorization.token.header.name"));
              
              if(authorizationHeader != null || 
                      !authorizationHeader.startsWith(environment.getProperty("authorization.token.header.name.prefix"))) {
                  filter.doFilter(req, res);
                  
                  return;
              }
              
              UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
              
              SecurityContextHolder.getContext().getAuthentication();
              filter.doFilter(req, res);
          }
      
          private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req) {
                  String authorizationHeader = req.getHeader(environment.getProperty("authorization.token.header.name"));
                  
                  if(authorizationHeader == null) {
                      return null;
                  }
                  
                  String token = authorizationHeader.replace(
                          environment.getProperty("authorization.token.header.name.prefix"), "");
                  String userId = Jwts.parser()
                          .setSigningKey(environment.getProperty("token.secret"))
                          .parseClaimsJws(token)
                          .getBody()
                          .getSubject();
                  if(userId == null) {
                      return null;
                  }
                  
              return new UsernamePasswordAuthenticationToken(userId, null, new ArrayList<>());
          }
          
      }
      

      我希望这段代码对你有用

      【讨论】:

      • 我已经在生成一个令牌。我只是想把这个令牌提取到另一个微服务中
      【解决方案4】:

      使用以下方法从 jwt 令牌中解析用户名

       final String authHeader = request.getHeader("Authorization");
          String username = null;
          String jwt = null;
      
          if (authHeader != null && authHeader.startsWith("Bearer")) {
              jwt = authHeader.substring(7);
              username = jwtUtil.extractUsername(jwt);
          }
      

      【讨论】:

        猜你喜欢
        • 2021-06-01
        • 2017-02-10
        • 2018-11-08
        • 2018-11-23
        • 2020-05-05
        • 2021-07-05
        • 2018-01-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多