JSON Web Token with Apache Shiro
Abstract
In this post, I will try to introduce you to the concept of JSON Web Tokens and show a way to use them in combination with Apache Shiro. The only premise is some basic understanding of Apache Shiro.
Let’s start right away with the question…
What is a JSON Web Token (JWT)?
JWT is an open standard (RFC 7519). It’s goal is to define a compact and self-contained way to transfer data. Let’s elaborate a bit more on the embolded terms.
compact: JWT’s are small and can be sent via URL / POST Request / HTTP Header. Also, smaller size means faster transmission.
self-contained: You can place all the necessary data in the payload. Thus reducing the amount of queries to a database.
Use Cases
A popular use of JWT’s is Single Sign On (SSO). Once a subject authenticates to a domain, it receives a JWT. This JWT is then presented everytime the subject requests protected resources.
Another popular use is data transmission. JWT’s are always hashed and signed. Because of this, you can always be sure of data integrity and the senders authenticity.
Structure
So, what does a JWT look like? There are three parts: header, payload and signature.
Header
Usually, the header contains two entries. The first one is the name of the hashing algorithm in use. The second is the type of the token.
Payload
The playload holds several “claims” about the entity that owns the token. There are three types of claims: reserved (predefined and optional), public (should be registered in IANA, or have a public name) and private (only used by your server and clients).
Signature
At last, there is the signature. It’s a combination of the encoded header and payload, signed using a secret. The secret may be any kind of information, for example the servers public key.
Example Token
Decoded:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//Header { "alg": "HS256", "typ": "JWT" } //Payload { "sub": "1234567890", "name": "John Doe", "admin": true } //Secret HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret ) |
Encoded. Dots separate the header, payload and secret.
1 |
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ |
Combine JWT and Apache Shiro
Now let’s see how we can use JWT in co-operation with Apache Shiro. The approach is simple. First, we get the necessary JAR’s. Second, we create a servlet to generate tokens. Finally, we create two filters. And then we are done!
Get a library
There are several JWT libraries available (see here). I’m using “Java JWT: JSON Web Token for Java and Android” (repo). It’s well designed and easy to use, just like Apache Shiro. Which is not surprising, since they have the same developer (Lez Hazlewood).
Maven Dependency:
1 2 3 4 5 6 |
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> </dependency> |
If you are not using any kind of build tool, download these: jjwt, jackson-core, jackson-databind. Note: jackson-* must be at least version 2.x.
Write the Components
Servlet: JWTProvider
The JWTProvider does exactly what the name suggests. Upon receiving a request, it generates a token and sends it back in the response body. In this example, we’ll only save the username of the current subject. Of course, you can (and should) use a variety of information. This is one of JWT’s advantages. You can put almost any kind of data in the payload.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
package src.servlet; import java.io.IOException; import java.security.Key; import javax.crypto.spec.SecretKeySpec; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.DatatypeConverter; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; /** * Servlet implementation class JWTProvider */ @WebServlet("/JWTProvider") public class JWTProvider extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public JWTProvider() { super(); } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { SignatureAlgorithm sigAlg = SignatureAlgorithm.HS256; byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("secret"); Key signingKey = new SecretKeySpec(apiKeySecretBytes, sigAlg.getJcaName()); System.out.println(request.getUserPrincipal().getName()); JwtBuilder builder = Jwts.builder() .setSubject(request.getUserPrincipal().getName()) .signWith(sigAlg, signingKey); response.getWriter().write(builder.compact()); response.setStatus(HttpServletResponse.SC_OK); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } |
Authentication Filter: JWTGuard
Using JWT, we want to secure our REST API. If an incoming request contains no token, the recommended approach is to simply deny the request. Let’s create a filter for that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import org.apache.shiro.web.filter.authc.AuthenticationFilter; public class JWTGuard extends AuthenticationFilter { @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED); return false; } } |
Access Control Filter: JWTVerifyingFilter
Now, if the request actually contains a token, we should see if it’s valid. Since we only save the current subject’s username, we should check if the incoming token contains one. If yes, additionally check if it equals the current subject’s username. And if any of the checks fails, we simply deny the request.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.DatatypeConverter; import org.apache.shiro.SecurityUtils; import org.apache.shiro.web.filter.AccessControlFilter; import io.jsonwebtoken.Jwts; 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; } } |
Shiro INI
So far, we declared the different classes. Now let’s configure Shiro to actually use them.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[main] jwtg = src.config.JWTGuard jwtv = src.config.JWTVerfyingFilter # Your config... [urls] /JWTProvider = jwtg # unauthenticated requests to /JWTProvider will be denied # REST # deny unverified tokens /rest/shop/browse/** = noSessionCreation, jwtv /rest/shop/add = noSessionCreation, jwtv /rest/shop/delete/* = noSessionCreation, jwtv |
That’s it!
With only little effort we made it possible for clients to get a token and let a server verify it. I should mention that the username-equals approach is not recommended.
Comment article
Recent posts






Comments
Miguel
It would be great if the complete example could be available in some GitHub repository.
As other comments said, there seems to be some missing parts to fully understand how JWT works with Shiro.
Where is the “noSessionCreation” mentioned in shiro.ini defined , for example?
A more complete example can be found here:
https://github.com/panchitoboy/shiro-jwt
However your approach seems to be “cleaner” to me… therefore a full example would be greatly appreciated !
Joe
Example seems to be missing an important part 🙁
jack
SecurityUtils.getSubject()
When did the current user join?