Spring Security授权时403错误:无效Base64编码字符串求助
问题描述
搭建Spring Security认证授权体系后,认证功能正常可获取JWT令牌(当前用户角色为ROLE_USER),但调用GET /api/users接口时返回403 Forbidden错误,错误信息:
{"error_token": "The input is not a valid base 64 encoded string"}
问题原因
1. JWT角色序列化与反序列化不匹配
- 认证过滤器(
CustomAuthentificationFilter)生成JWT时,用Collectors.joining()将用户角色集合拼接成单个字符串(例如单角色时存储为"ROLE_USER",多角色时为"ROLE_ADMIN,ROLE_USER")。 - 授权过滤器(
CustomAuthorizationFilter)解析JWT时,尝试用decodedJWT.getClaim("roles").asArray(String.class)将角色解析为字符串数组。但JWT中实际存储的是单个字符串,JWT库会尝试将该字符串当作base64编码的数组内容解码,最终抛出"不是有效的base64编码字符串"的错误。
2. 授权过滤器逻辑错误
filterChain.doFilter(request, response)被放在遍历角色的forEach循环内部,会导致过滤器链被多次执行,引发异常。
3. 安全配置角色校验规则错误
hasAnyRole方法会自动给角色前缀添加ROLE_,但配置中传入的参数是"ROLE_ADMIN","ROLE_USER",最终会变成匹配ROLE_ROLE_ADMIN和ROLE_ROLE_USER,导致角色校验失败。
解决步骤
1. 修复JWT角色序列化方式(认证过滤器)
将角色集合序列化为字符串数组,替换原有的拼接逻辑:
修改CustomAuthentificationFilter中生成access_token和refresh_token的withClaim代码:
// 原代码 .withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining())) // 修改为 .withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).toArray(String[]::new))
2. 修复授权过滤器逻辑
将filterChain.doFilter(request, response)移出角色遍历循环,确保过滤器链仅执行一次:
修改CustomAuthorizationFilter核心逻辑:
try { String token = authorizationHeader.substring("Bearer ".length()); Algorithm algorithm = Algorithm.HMAC256("secret".getBytes()); JWTVerifier jwtVerifier = JWT.require(algorithm).build(); DecodedJWT decodedJWT = jwtVerifier.verify(token); String username = decodedJWT.getSubject(); String[] roles = decodedJWT.getClaim("roles").asArray(String.class); Collection<SimpleGrantedAuthority> authorities = new ArrayList<>(); // 遍历收集角色 stream(roles).forEach(role -> { authorities.add(new SimpleGrantedAuthority(role)); }); // 初始化认证信息并执行过滤器链(移出循环) UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities); SecurityContextHolder.getContext().setAuthentication(authenticationToken); filterChain.doFilter(request, response); } catch (Exception exception) { log.error("error loggin in: {}", exception.getMessage()); response.setHeader("error", exception.getMessage()); response.setStatus(FORBIDDEN.value()); Map<String, String> error = new HashMap<>(); error.put("error_token", exception.getMessage()); response.setContentType(APPLICATION_JSON_VALUE); new ObjectMapper().writeValue(response.getOutputStream(), error); }
3. 修复安全配置角色校验规则
针对hasAnyRole的自动前缀特性,修改授权规则的角色参数:
// 原代码 http.authorizeRequests().antMatchers("/api/users").hasAnyRole("ROLE_ADMIN","ROLE_USER"); // 修改为(自动添加ROLE_前缀) http.authorizeRequests().antMatchers("/api/users").hasAnyRole("ADMIN","USER"); // 或改用hasAnyAuthority(直接使用完整角色名,不自动加前缀) // http.authorizeRequests().antMatchers("/api/users").hasAnyAuthority("ROLE_ADMIN","ROLE_USER");
内容的提问来源于stack exchange,提问作者user13906062
相关产品推荐
相关产品推荐

