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:无法识别的令牌的主要内容,如果未能解决你的问题,请参考以下文章