I am working on a application where I have implemented a java Restful Backend Sercvice with Apache Shiro authentication. I can now get a user to register and log in successfully using password and salt backed by my database. Now i want to improve on this by adding JWT authentication.
The scenario would be:
To implement this functionality i followed: JSON Web Token with Apache Shiro
The whole job is performed by the 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;
}
}
However, i am facing the following problems:
As you can see the secret used to generate the jwt signature is the same 'secret' for all users. In every example i have found regarding jwt token signature generation and authentication, they use the same secret for all users when generating the jwt signature.
In my implementation i would like to use the salt stored per user in the database to generate the jwt signature and then verify it. So when generating the jwt per user i use the following function which uses user salt to create the jwt signature.
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();
}
}
In my case, in order validate the jwt signature i need get the salt per user. So, i changed the implementation to of JWTVerifyingFilter to the following:
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;
}
}
However calling, Apache Shiro's SecurityUtils.getSubject()
returns a null object. I tried to implement a Singleton Class to get always the same subject from SecurityUtils (if there is one subject return it else call SecurityUtils.getSubject()) with no success. In this later implementation only one user can login to system. Using another browser the backend reported user was already logged in with credential those of the user previously logged by a different browser.
Questions:
Thanks in advance for any answers.
The problem was at my shiro.ini
I had enabled the option
securityManager.sessionManager.sessionIdCookieEnabled = false
I commented it out and everything works fine. Now i have access to subject through shiro's SecurityUtils.getSubject();