com.fasterxml.jackson.core.JsonParseException:无法识别的令牌

Posted

技术标签:

【中文标题】com.fasterxml.jackson.core.JsonParseException:无法识别的令牌【英文标题】:com.fasterxml.jackson.core.JsonParseException: Unrecognized token 【发布时间】:2018-10-29 04:51:05 【问题描述】:

我正在使用 spring boot(使用 JWT 和 spring security),我的目标是将令牌内经过身份验证的用户的 ID 发送到 angular5(前端)。

这是 JWTAuthenticationFilter.java

     public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter 

         private AuthenticationManager authenticationManager;

        @Autowired
        private UserRepo userRepo;

        @Autowired
         private AccountService accountService;

         @Autowired
          public JWTAuthenticationFilter(AuthenticationManager authenticationManager) 
          this.authenticationManager = authenticationManager;
       

        @Override
        @Autowired
        public void setAuthenticationManager(AuthenticationManager authenticationManager) 
            super.setAuthenticationManager(authenticationManager);
        

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException 

        AppUser appUser = null;
        try 
            appUser=new ObjectMapper().readValue(request.getInputStream(),AppUser.class);
         catch (IOException e) 
           throw new RuntimeException(e.getMessage());
        

         return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(appUser.getUsername(),appUser.getPassword()));
    

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException 

        User springUser = (User)authResult.getPrincipal();
        String jwt = Jwts.builder()
        .setSubject(springUser.getUsername())
                .setExpiration(new Date(System.currentTimeMillis()+SecurityConstants.EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS256,SecurityConstants.SECRET)
                .claim("roles",springUser.getAuthorities())
                .compact();


         AppUser app = userRepo.findByUsername(springUser.getUsername());
         Long id = app.getId();

         ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jsonJwt = objectMapper.readTree(jwt);
        ((ObjectNode)jsonJwt).put("userId", id);

         response.addHeader(SecurityConstants.HEADER_STRING,SecurityConstants.TOKEN_PREFIX+objectMapper.writeValueAsString(jsonJwt));

    

这是 JWTAutorizationFilter.java

 package interv.Web.service;

import interv.Web.security.SecurityConstants;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

public class JWTAutorizationFilter extends OncePerRequestFilter


    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException 


        httpServletResponse.addHeader("Access-Control-Allow-Origin","*");
        httpServletResponse.addHeader("Access-Control-Allow-Headers", " Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization");
        httpServletResponse.addHeader("Access-Control-Expose-Headers",
                "Access-Control-Allow-Origin, Access-Control-Allow-Credentials,Authorization");
        httpServletResponse.addHeader("Access-Control-Allow-Methods","GET,PUT,POST,DELETE");


        String jwtToken = httpServletRequest.getHeader(SecurityConstants.HEADER_STRING);

         if(httpServletRequest.getMethod().equals("OPTIONS"))
             httpServletResponse.setStatus(httpServletResponse.SC_OK);
        
         else 
             if(jwtToken==null || !jwtToken.startsWith(SecurityConstants.TOKEN_PREFIX))

                filterChain.doFilter(httpServletRequest,httpServletResponse);

                 return ;
             

             Claims claims = Jwts.parser()
                    .setSigningKey(SecurityConstants.SECRET)
                    .parseClaimsJws(jwtToken.replace(SecurityConstants.TOKEN_PREFIX,""))
                    .getBody();

            String username = claims.getSubject();
            ArrayList<Map<String,String>> roles = (ArrayList<Map<String,String>>)claims.get("roles");


            Collection<GrantedAuthority> authorities = new ArrayList<>();
            roles.forEach(r->
                authorities.add(new SimpleGrantedAuthority(r.get("authority")));
             );

            UsernamePasswordAuthenticationToken authenticationToken=
                    new UsernamePasswordAuthenticationToken(username,null,authorities);
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            filterChain.doFilter(httpServletRequest,httpServletResponse);
         

        

    

在运行应用程序时我没有收到任何错误,但是当我尝试进行身份验证时,我在控制台中收到了这个奇怪的错误:

     ERROR 4980 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'eyJhbGciOiJIUzI1NiJ9': was expecting ('true', 'false' or 'null')
 at [Source: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTUyNzUyMjQxNywicm9sZXMiOlt7ImF1dGhvcml0eSI6IkFETUlOIn1dfQ.BtaWfqSy9xyDdZrEsJD6iJRVLyTpHEVGYL1NVR670Ts; line: 1, column: 21]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1702) ~[jackson-core-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:558) ~[jackson-core-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.java:2839) ~[jackson-core-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleOddValue(ReaderBasedJsonParser.java:1903) ~[jackson-core-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:749) ~[jackson-core-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:3850) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3799) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:2397) ~[jackson-databind-2.8.10.jar:2.8.10]
    at interv.Web.security.JWTAuthenticationFilter.successfulAuthentication(JWTAuthenticationFilter.java:83) ~[classes/:na]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:240) ~[spring-security-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at interv.Web.service.JWTAutorizationFilter.doFilterInternal(JWTAutorizationFilter.java:43) ~[classes/:na]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]

在弧形扩展中:

   
"timestamp": 1526663679085,
"status": 500,
"error": "Internal Server Error",
"exception": "com.fasterxml.jackson.core.JsonParseException",
"message": "Unrecognized token 'eyJhbGciOiJIUzI1NiJ9': was expecting ('true', 'false' or 'null') at [Source: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTUyNzUyNzY3OCwicm9sZXMiOlt7ImF1dGhvcml0eSI6IkFETUlOIn1dfQ.VibLFbRhNapwK2lw8pAFBvayFTI6CUY1tVNmVHPwBSE; line: 1, column: 21]",
"path": "/login"

我唯一清楚的是错误来自以下几行:

         ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jsonJwt = objectMapper.readTree(jwt);
        ((ObjectNode)jsonJwt).put("userId", id);

response.addHeader(SecurityConstants.HEADER_STRING,SecurityConstants.TOKEN_PREFIX+objectMapper.writeValueAsString(jsonJwt));

我试图改变如何将 JsonNode 转换为字符串的方式,但没有结果。

PS : 我在获取认证用户的 id 时没有问题。

有什么想法吗?

编辑

这是 SecurityConfig.java :

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)

public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 

              auth.userDetailsService(userDetailsService)
              .passwordEncoder(bCryptPasswordEncoder);
    


    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.csrf().disable();
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.authorizeRequests().antMatchers("/login/**").permitAll();
        http.authorizeRequests().antMatchers(HttpMethod.GET,"/ListProjects/**").hasAuthority("ADMIN");
        http.authorizeRequests().anyRequest().authenticated()
                .and()
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint());
        http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll(); //allow CORS option calls
       // http.addFilter(new JWTAuthenticationFilter(authenticationManager()));
        http.addFilter(jwtAuthenticationFilter());
        http.addFilterBefore(new JWTAutorizationFilter(), UsernamePasswordAuthenticationFilter.class);

    




    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() 
        return new AuthenticationEntryPoint() 
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException, IOException 
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
                        "Unauthorized: Authentication token was either missing or invalid.");
            
        ;
    


    @Bean
    public JWTAuthenticationFilter jwtAuthenticationFilter() throws Exception 
        return new JWTAuthenticationFilter(authenticationManager());
    

【问题讨论】:

【参考方案1】:

您需要创建自己的实现 TokenEnhancer 接口的 UserTokenEnhancer。

public class UserTokenEnhancer implements TokenEnhancer 

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) 
        User user = (User) authentication.getPrincipal();
        final Map<String, Object> enhancerInfo = new HashMap<>();

        enhancerInfo.put("userInfo", "some_user_info");

        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(enhancerInfo);

        return accessToken;
    


之后,您需要在扩展 AuthorizationServerConfigurerAdapter 的 AuthServerConfig 中添加以下代码

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter 
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
 endpoints
            // ...
            .tokenEnhancer(tokenEnhancer());


    @Bean
    public TokenEnhancer tokenEnhancer() 
        return new UserTokenEnhancer();
    

就是这样。

【讨论】:

你的意思是我不需要做什么?但是我没有在我的项目中使用任何 Auth2 配置,这可能是个问题吗?我已经添加了我在帖子中使用的 securityConfig。 有什么想法吗?

以上是关于com.fasterxml.jackson.core.JsonParseException:无法识别的令牌的主要内容,如果未能解决你的问题,请参考以下文章