gpt4 book ai didi

rest - Apache Shiro 和 JWT 实现问题,当每个用户使用不同的 secret 时

转载 作者:行者123 更新时间:2023-12-02 04:27:56 32 4
gpt4 key购买 nike

我正在开发一个应用程序,在该应用程序中我实现了一个带有 Apache Shiro 身份验证的 java Restful 后端服务。我现在可以让用户使用我的数据库支持的密码和盐成功注册和登录。现在我想通过添加 JWT 身份验证来改进这一点。

场景是:

  1. 用户尝试使用用户名和密码登录
  2. 成功发送凭据并且shiro登录用户后,后端生成一个jwt token 并将其发送回客户端
  3. 在每个新请求中,客户端将在上一步中收到的 jwt token 发送到服务器以进行身份​​验证。
  4. Shiro Filter 检查请求中包含的 token 。如果有效,则返回错误消息。

为了实现这个功能,我遵循了: JSON Web Token with Apache Shiro

整个工作由 JWTVerifyingFilter 执行:

public class JWTVerifyingFilter extends AccessControlFilter {

@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse arg1, Object arg2) throws Exception {
boolean accessAllowed = false;
HttpServletRequest httpRequest = (HttpServletRequest) request;
String jwt = httpRequest.getHeader("Authorization");
if (jwt == null || !jwt.startsWith("Bearer ")) {
return accessAllowed;
}
jwt = jwt.substring(jwt.indexOf(" "));
String username = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary("secret"))
.parseClaimsJws(jwt).getBody().getSubject();
String subjectName = (String) SecurityUtils.getSubject().getPrincipal();
if (username.equals(subjectName)) {
accessAllowed = true;
}
return accessAllowed;
}

@Override
protected boolean onAccessDenied(ServletRequest arg0, ServletResponse arg1) throws Exception {
HttpServletResponse response = (HttpServletResponse) arg1;
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return false;
}

}

但是,我面临着以下问题:

如您所见,用于生成 jwt 签名的 secret 对于所有用户都是相同的“ secret ”。在我发现的关于 jwt token 签名生成和身份验证的每个示例中,它们在生成 jwt 签名时对所有用户使用相同的 secret 。

在我的实现中,我想使用数据库中每个用户存储的盐来生成 jwt 签名,然后对其进行验证。因此,在为每个用户生成 jwt 时,我使用以下函数,该函数使用用户 salt 创建 jwt 签名。

    public class JWTProvider {

private JWTProvider() {

}

public static String getJWTToken(User user) {

System.out.println("JWT Provider FIRED");
SignatureAlgorithm sigAlg = SignatureAlgorithm.HS512;

byte[] apiKeySecretBytes = Base64.decode(user.getSalt());

Key signingKey = new SecretKeySpec(apiKeySecretBytes, sigAlg.getJcaName());
Date date = new Date();
JwtBuilder builder = Jwts.builder()
.setSubject(user.getUsername())
.claim("FirstName", user.getFirstName())
.claim("LastName", user.getLastName())
.setIssuedAt(date)
.setExpiration(new Date(date.getTime() + 24 * 60 * 60 * 1000)) //Expires in One Days
.signWith(sigAlg, signingKey);

System.out.println("Generated JWT: " + builder.compact());
return builder.compact();
}

}

在我的例子中,为了验证 jwt 签名,我需要为每个用户获取 salt。因此,我将 JWTVerifyingFilter 的实现更改为以下内容:

public class JWTVerifyingFilter extends AccessControlFilter {

@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) {
System.out.println("JWT Verifier Fired.....");
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String jwt = httpRequest.getHeader("Authorization");
if (jwt == null || !jwt.startsWith("Bearer ")) {
System.out.println("DEn e brika prama: ");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
System.out.println("Found Something");
jwt = jwt.substring(jwt.indexOf(" "));
System.out.println("JWT: " + jwt);

User user = (User) SecurityUtils.getSubject().getPrincipal();
System.out.println("MMMMMMMMMMMMMM " + user.getUsername() + "jhhhhhhhhhhhhhh" + user.getSalt());

String subjectName = ((User) SecurityUtils.getSubject()).getPrincipal();
System.out.println("Subject: " + subjectName);
String username = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary("secret"))
.parseClaimsJws(jwt).getBody().getSubject();
System.out.println("UserNAeme: " + username);
System.out.println("Subject: " + subjectName);
if (username.equals(subjectName)) {
response.setStatus(HttpServletResponse.SC_OK);
return true;
}
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}

@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return false;
}
}

无论如何调用,Apache Shiro 的SecurityUtils.getSubject() 都会返回一个null 对象。我试图实现一个 Singleton 类以从 SecurityUtils 中始终获得相同的主题(如果有一个主题返回它,否则调用 SecurityUtils.getSubject())但没有成功。在后来的实现中,只有一个用户可以登录系统。使用另一个浏览器,后端报告的用户已经使用先前由其他浏览器登录的用户的凭据登录。

问题:

  1. 在所有用户的 jwt 身份验证中始终使用相同的 secret 是否可以?
  2. apache shiro 在哪里保存登录用户的信息,我如何从 restful 后端 java 服务访问它们?
  3. 每个用户使用不同的 jwt 密码的 Apache Shiro 的任何完整示例?实现这个的最佳方法是什么。
  4. 我是否需要像 redis 这样的内存存储来让经过身份验证的用户在登录时保留他们的 salt 并在后续请求中从那里获取他们的 salt??

提前感谢您的回答。

最佳答案

问题出在我的shiro.ini

我已经启用了这个选项

securityManager.sessionManager.sessionIdCookieEnabled = false

我把它注释掉了,一切正常。现在我可以通过 shiro 的 SecurityUtils.getSubject();

访问主题

关于rest - Apache Shiro 和 JWT 实现问题,当每个用户使用不同的 secret 时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52038723/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com