代码之家  ›  专栏  ›  技术社区  ›  Nuñito Calzada

Spring Boot with JWT:访问此资源需要完全身份验证

  •  -1
  • Nuñito Calzada  · 技术社区  · 3 月前

    我有这个控制器:

        @GetMapping("/daily")
        @PreAuthorize("hasRole('BASIC')")
        public ResponseEntity<TransitsResponse> dailyT(
                @RequestHeader(value = "Authorization") String authHeader)
                throws UnirestException, JsonProcessingException {
    ....
    }
    

    这个类:

    @Component
    @Slf4j
    public class AuthEntryPointJwt implements AuthenticationEntryPoint {
    
        //private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);
    
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
    
            if (!request.getServletPath().equalsIgnoreCase("/")) {
                log.error("Unauthorized error: {} with request {} from {}",
                        authException.getMessage(),
                        request.getServletPath(),
                        RequestUtils.getClientIp(request));
            }
    
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    
            final Map<String, Object> body = new HashMap<>();
            body.put("status", HttpServletResponse.SC_UNAUTHORIZED);
            body.put("error", "Unauthorized");
            body.put("message", authException.getMessage());
            body.put("path", request.getServletPath());
    
            final ObjectMapper mapper = new ObjectMapper();
            mapper.writeValue(response.getOutputStream(), body);
    
        }
    }
    

    这个:

    @Slf4j
    public class AuthTokenFilter extends OncePerRequestFilter {
    
      //@Autowired
      private final JwtUtils jwtUtils;
    
      //@Autowired
      private final UserDetailsServiceImpl userDetailsService;
    
        public AuthTokenFilter(JwtUtils jwtUtils, UserDetailsServiceImpl userDetailsService) {
            this.jwtUtils = jwtUtils;
            this.userDetailsService = userDetailsService;
        }
    
        //private static final Logger  = LoggerFactory.getLogger(AuthTokenFilter.class);
    
      @Override
      protected void doFilterInternal(@NotNull HttpServletRequest request,
                                      @NotNull HttpServletResponse response,
                                      @NotNull FilterChain filterChain)
    
          throws ServletException, IOException {
    
        try {
    
          log.info(String.valueOf(request));
    
          String jwt = parseJwt(request);
    
          log.info("jwt: " + jwt);
    
          if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
    
            String username = jwtUtils.getUserNameFromJwtToken(jwt);
    
            log.info("username: " + username);
    
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
    
            log.info("userDetails: " + userDetails);
    
            UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(
                    userDetails,
                    null,
                    userDetails.getAuthorities());
    
            log.info("authentication: " + authentication);
    
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
    
            log.info("authentication: " + authentication);
    
            SecurityContextHolder.getContext().setAuthentication(authentication);
    
            log.info("authentication: " + authentication);
    
          }
    
        } catch (Exception e) {
          log.error("Cannot set user authentication: {}", e);
        }
    
        filterChain.doFilter(request, response);
      }
    
      private String parseJwt (HttpServletRequest request) {
    
        String headerAuth = request.getHeader("Authorization");
    
        log.info("headerAuth: " + headerAuth);
    
        if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
          return headerAuth.substring(7);
        }
    
        return null;
      }
    
    }
    

    @Component
    @Slf4j
    public class JwtUtils {
        //private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
    
        @Value("${bezkoder.app.jwtSecret}")
        private String jwtSecret;
    
        @Value("${bezkoder.app.jwtExpirationMs}")
        private int jwtExpirationMs;
    
        public String generateJwtToken(Authentication authentication) {
    
            log.info("generateJwtToken {} ", authentication);
    
            UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
    
            log.info("userPrincipal {} ", userPrincipal);
    
            LocalDate nowPlus20Years = LocalDate.now().plusYears(20);
    
            log.info("nowPlus20Years {} ", nowPlus20Years);
    
            String jwt = Jwts.builder()
                    .setSubject((userPrincipal.getUsername()))
                    .setIssuedAt(new Date())
                    //.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
                    .setExpiration(Date.from(nowPlus20Years
                            .atStartOfDay(java.time.ZoneId.systemDefault()).toInstant()))
                    .signWith(key(), SignatureAlgorithm.HS256)
                    .compact();
    
            log.info("jwt {} ", jwt);
    
            return jwt;
        }
    
        private Key key() {
            return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret));
        }
    
        public String getUserNameFromJwtToken(String token) {
    
            log.info("getUserNameFromJwtToken {} ", token);
    
            String str = Jwts.parserBuilder().setSigningKey(key()).build()
                    .parseClaimsJws(token).getBody().getSubject();
    
            log.info("str {} ", str);
    
            return str;
    
        }
    
        public boolean validateJwtToken(String authToken) {
            try {
                log.info("validateJwtToken {} ", authToken);
                Jwts.parserBuilder().setSigningKey(key()).build().parse(authToken);
                log.info("JWT token is valid.");
                return true;
            } catch (MalformedJwtException e) {
                log.error("Invalid JWT token: {}", e.getMessage());
            } catch (ExpiredJwtException e) {
                //FIXME
                log.error("JWT token is expired: {}", e.getMessage());
                return true;
            } catch (UnsupportedJwtException e) {
                log.error("JWT token is unsupported: {}", e.getMessage());
            } catch (IllegalArgumentException e) {
                log.error("JWT claims string is empty: {}", e.getMessage());
            }
    
            return false;
        }
    }
    

    在我的本地主机上工作正常,但在另一台服务器上我无法连接。我在日志上看到了这一点:

    2025-02-09 16:00:37.583 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(49) - org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@333422c4
    2025-02-09 16:00:37.585 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@parseJwt(94) - headerAuth: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJudWxscG9pbnRlciIsImlhdCI6MTczOTEwODUyMiwiZXhwIjoyMzcwMjExMjAwfQ.lLmZum2iWox4T5lt7KYxZTXorm-VQjB8HVsw8BCdt56
    2025-02-09 16:00:37.586 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(53) - jwt: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJudWxscG9pbnRlciIsImlhdCI6MTczOTEwODUyMiwiZXhwIjoyMzcwMjExMjAwfQ.lLmZum2iWox4T5lt7KYxZTXorm-VQjB8HVsw8BCdt56
    2025-02-09 16:00:37.587 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.JwtUtils@validateJwtToken(74) - validateJwtToken eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJudWxscG9pbnRlciIsImlhdCI6MTczOTEwODUyMiwiZXhwIjoyMzcwMjExMjAwfQ.lLmZum2iWox4T5lt7KYxZTXorm-VQjB8HVsw8BCdt56 
    2025-02-09 16:00:37.591 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.JwtUtils@validateJwtToken(76) - JWT token is valid.
    2025-02-09 16:00:37.593 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.JwtUtils@getUserNameFromJwtToken(61) - getUserNameFromJwtToken eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJudWxscG9pbnRlciIsImlhdCI6MTczOTEwODUyMiwiZXhwIjoyMzcwMjExMjAwfQ.lLmZum2iWox4T5lt7KYxZTXorm-VQjB8HVsw8BCdt56 
    2025-02-09 16:00:37.600 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.JwtUtils@getUserNameFromJwtToken(66) - str nullpointer 
    2025-02-09 16:00:37.601 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(59) - username: nullpointer
    2025-02-09 16:00:37.607 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(63) - userDetails: com.taradell.security.services.UserDetailsImpl@56736d20
    2025-02-09 16:00:37.607 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(71) - authentication: UsernamePasswordAuthenticationToken [Principal=com.taradell.security.services.UserDetailsImpl@56736d20, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_SUBSCRIBED]]
    2025-02-09 16:00:37.608 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(75) - authentication: UsernamePasswordAuthenticationToken [Principal=com.taradell.security.services.UserDetailsImpl@56736d20, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=83.44.48.136, SessionId=null], Granted Authorities=[ROLE_SUBSCRIBED]]
    2025-02-09 16:00:37.612 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(79) - authentication: UsernamePasswordAuthenticationToken [Principal=com.taradell.security.services.UserDetailsImpl@56736d20, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=83.44.48.136, SessionId=null], Granted Authorities=[ROLE_SUBSCRIBED]]
    2025-02-09 16:00:37.633 [http-nio-8081-exec-3] ERROR [] c.m.security.jwt.AuthEntryPointJwt@commence(29) - Unauthorized error: Full authentication is required to access this resource with request /error from 83.44.48.136
    
    1 回复  |  直到 3 月前
        1
  •  2
  •   Roar S.    3 月前

    错误指示路径 /error 缺乏 .permitAll() 在里面 SecurityFilterChain . 因此,将此添加到SecurityFilterChain中:

    .requestMatchers("/error").permitAll().
    

    OP确认这解决了问题。