JWT 身份验证导致与常规身份验证发生冲突。我无法从登录页面登录
Posted
技术标签:
【中文标题】JWT 身份验证导致与常规身份验证发生冲突。我无法从登录页面登录【英文标题】:JWT Authentication causes conflicts with regular authentication. I can't log in from the login page 【发布时间】:2021-05-16 23:19:54 【问题描述】:在谈到 Spring Security 时,我是一个新手,特别是 JWT 和 CORS,所以如果我没有把这件事说清楚,我提前道歉。
我们被要求制作一个模拟私人诊所网站的应用程序,患者可以在该网站上预约医生并从药房购买产品。医生可以在数据库中介绍有关其患者的信息。我们的项目也有一个 Restful API,可以通过移动应用程序(或 Postman)访问。 API 的作用是显示我们存储在数据库中的产品列表。
所有用户都可以通过使用 Spring Security 的登录表单登录。另一方面,如果我们想要检索我们的 API 的信息,除了 Spring Security 之外,还使用了 CORS 和 JWT。
当我设置一个自定义授权过滤器时,问题就出现了,我们的老师让我们这样做(我已经评论了这样做的行)。我们可以完美地使用 Postman 访问我们的 API:我们使用管理员用户登录并将授权令牌传递给我们的 API 路由,作为回报,我们得到产品列表。但是当过滤器工作时,我们不能再使用我们网站的登录表单进行身份验证。整个过程是这样的:
-
应用程序从主页开始 (localhost:8080/inicio)。
在主页上有一个“登录”按钮,当用户未通过身份验证时会出现。单击它会将我们带到登录表单。
在登录表单 (localhost:8080/auth/login) 中,我们填写所有必要的字段,以便我们以数据库用户身份登录(在本例中,用户名:admin,密码:admin)。
我们提交表单,该表单将我们带到负责身份验证过程的请愿书 (localhost:8080/login/login-post)。
在进程结束时,我们被重定向回主页。当用户通过身份验证时,“登录”按钮应显示为“注销”。但事实并非如此。我们无法导航到经过身份验证的用户不应访问的其他页面。
控制台不提供任何错误消息,它所做的只是将我带回主页,而无需对用户进行身份验证。
这是我的 Spring Security 配置类:
@Autowired
@Qualifier("userService")
private UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
return super.authenticationManagerBean();
@Bean
public BCryptPasswordEncoder passwordEncoder()
return new BCryptPasswordEncoder();
@Override
protected void configure(HttpSecurity http) throws Exception
http
.csrf().disable()
// .addFilterAfter(new JWTAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "/css/**", "/img/**", "/js/**", "/vendor/**", "/inicio/**", "/pacientes/altaPaciente/**", "/pacientes/addPaciente/**", "/auth/**").permitAll()
.antMatchers(HttpMethod.GET, "/authRest/**").permitAll()
.antMatchers(HttpMethod.POST, "/authRest/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/auth/login")
.defaultSuccessUrl("/inicio/", true)
.loginProcessingUrl("/auth/login-post")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/auth/login?logout")
.permitAll();
还有我的 JWT 授权过滤器:
private final String HEADER = "Authorization";
private final String PREFIX = "Bearer ";
private final String SECRET = "mySecretKey";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException
try
if (checkJWTToken(request, response))
Claims claims = validateToken(request);
if (claims.get("authorities") != null)
setUpSpringAuthentication(claims);
else
SecurityContextHolder.clearContext();
else
SecurityContextHolder.clearContext();
chain.doFilter(request, response);
catch(ExpiredJwtException | UnsupportedJwtException | MalformedJwtException e)
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
return;
private Claims validateToken(HttpServletRequest request)
String jwtToken = request.getHeader(HEADER).replace(PREFIX, "");
return Jwts.parser().setSigningKey(SECRET.getBytes()).parseClaimsJws(jwtToken).getBody();
private void setUpSpringAuthentication(Claims claims)
@SuppressWarnings("unchecked")
List<String> authorities = (List<String>) claims.get("authorities");
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
claims.getSubject(),
null,
authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())
);
SecurityContextHolder.getContext().setAuthentication(auth);
private boolean checkJWTToken(HttpServletRequest request, HttpServletResponse res)
String authenticationHeader = request.getHeader(HEADER);
if (authenticationHeader == null || !authenticationHeader.startsWith(PREFIX))
return false;
return true;
编辑:根据要求,这是我尝试使用 Web 表单作为数据库中的现有用户登录时得到的日志:https://pastebin.com/7SYX2MZF
【问题讨论】:
为什么在 Spring Security 中已经内置了一个自定义 jwt 过滤器? docs.spring.io/spring-security/site/docs/current/reference/… 这是我们老师向我们展示的用于使 JWT 身份验证工作的一个,我们甚至不知道 Spring Security 一开始就有一个。但是,如果它已经在 Spring Security 配置中定义,那么它对我的 Resful API 的安全性没有任何作用,我可以使用我想要访问它的任何授权令牌。 在目前的状态下,这个问题需要更清楚the only one working is the JWT Authentication
可能意味着任何事情,EXACTLY
不起作用,明确的重现步骤。可以登录吗?发布 spring 安全调试日志,并发布您的请求是什么样的。没有日志或重现步骤,没有人能够回答。
我会尽力重做我的问题,所以它会变得更清楚,但正如我所说,这是一个我不太熟悉的话题,我很难理解。
您可以通过激活调试日志开始***.com/a/47729991/1840146 重新启动服务器,执行您的请求,然后使用调试日志更新问题
【参考方案1】:
故障可能是(经过讨论)
这里的某个地方:
if (checkJWTToken(request, response))
Claims claims = validateToken(request);
if (claims.get("authorities") != null)
setUpSpringAuthentication(claims);
else
SecurityContextHolder.clearContext();
else
SecurityContextHolder.clearContext();
检查checkJWTToken
中是否存在Authorization
标头,如果没有,则清除当前的SecurityContext
,这意味着它将删除存在的任何主体。
这将删除以前登录的任何人,这反过来又是最初登录时构造的主体。
所以你登录,安全上下文由主体填充,然后在下一个过滤器中突然被删除。
【讨论】:
以上是关于JWT 身份验证导致与常规身份验证发生冲突。我无法从登录页面登录的主要内容,如果未能解决你的问题,请参考以下文章