我正在开发一个应用程序,我用Apache Shiro身份验证实现了一个java Restful Backend Sercvice。我现在可以让用户注册并使用我的数据库支持的密码和salt成功登录。现在我想通过添加JWT身份验证来改进这一点。
场景将是:
为了实现这个功能,我遵循: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签名的秘密对于所有用户来说都是相同的“秘密”。在我发现的关于jwt令牌签名生成和认证的每个示例中,它们在生成jwt签名时对所有用户使用相同的秘密。
在我的实现中,我想使用数据库中每个用户存储的salt来生成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签名,我需要获得每个用户的盐。所以,我将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())没有成功。在此后续实现中,只有一个用户可以登录系统。使用另一个浏览器后端报告的用户已经使用以前由其他浏览器记录的用户的凭据登录。
问题:
提前感谢您的任何答案。
问题发生在我的shiro.ini
我启用了该选项
securityManager.sessionManager.sessionIdCookieEnabled = false
我评论了它,一切正常。现在我可以通过shiro的SecurityUtils.getSubject();
访问主题