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

时间:2018-05-18 17:28:54

标签: java spring spring-boot jwt token

我正在使用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]

在Arc扩展中:

   {
"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 个答案:

答案 0 :(得分:1)

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

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

}

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

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

这就是全部。