Secure your REST APIs with Spring Security & Symmetric Key Encryption

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome back Friends Dan Vega here your friendly neighborhood spring developer Advocate and today we're going to be talking about securing your rest endpoints using Json web tokens and symmetric key encryption now what the heck just came out of your mouth then I know I said the same thing but what we're going to do is kind of break this down for you so in a previous tutorial which I will link up here and in the description below we did this we secured our rest endpoints using jwts and something called asymmetric encryption so there's asymmetric and symmetric what's the difference Dan we're going to be using spring Securities oauth 2 resource server which utilizes cryptographic signatures to validate the tokens this simplifies a lot of things because you can eliminate the need for some separate authorization server so this is great so we have asymmetric and symmetric so asymmetric which we used in the previous tutorial uses two different Keys it uses the public and a private key one to go ahead and sign the token and then one to go ahead and validate the token in a symmetric key we only have one single key the same key is used to both sign the token and validate the token this as you can imagine is a little bit faster and a little bit simpler so when would I want to use this Dan let's just say you have a simple application maybe you have a spa front end and a single back end we're not trying to replicate this back end over instances we have no sessions or therefore sticky sessions this is a great use case for that again it's a little bit easier one key we don't have to deal with generating these uh different public and private key pairs so it just simplifies a lot of things so how are we going to accomplish this today I'm going to walk you over to the GitHub repository this will have all the final code in it if you want to go ahead and check that out you can but we are also going to build this from scratch we're going to start a new spring boot 3 project there have been some changes if you've been paying attention to this channel in Spring Security Six so we'll take a look at those changes as we start to build out our security configuration so uh this is something that you guys have been asking for so I really hope you enjoy that and with that let's dive into the tutorial all right so the first thing is if you want to grab the completed source code you could head over to github.com danvegas JWT symmetric Dash key here there's some information about the tutorial that we're going through today as well as how to run the application and a quick thank you and shout out to my friend Daniel Garnier who really helped me uh on this particular tutorial so go ahead and check that out but if you want to follow along you can build this with me we're going to start every project like we do over at start.spring.io I'm going to choose a maven project I'm going to use the latest version of spring boot which is 3.0.1 at the time of this recording I'm going to go ahead and set a group of dev.dan Vega we'll call this JWT symmetric key and this is a JWT demo using whoops using symmetric key encryption so that looks good we're using jar we're using Java 17 and then we just need a couple of dependencies so I'm going to choose spring web and also you might think to go ahead and grab Spring Security we actually don't need to grab Spring Security because we're going to pick the oauth 2 resource server and if we go do a quick explore here we can say uh we can actually jump into this one here I can do that in the palm.xml once we get into the project but you'll see that that actually brings in the Spring Security dependencies that we need so this is really all we need to get started so what I'm going to do is go ahead and generate this it's going to download a zip file for me I'm going to open this up in my favorite IDE IntelliJ IDEA you can open it up in whatever ID or text editor you are most productive in all right so I'm going to start this tutorial by taking a look at the Palm again I mentioned before we have the spring boot starter oau 2 resource to Resource server if we go ahead and jump into that we can see that that brings in things like spring boots starter Spring Security config Spring Security core the resource server things like uh oauth 2 Jose so there's a bunch of stuff in there so all the dependencies that we need we brought in by just choosing that O2 resource server so the first thing that I want to do is actually go ahead and create a couple of classes let's start with a controller so I'm going to create a new package I'm going to call this controller I'm going to create a new Java class in here I'm going to call this home controller at the end of the day this is going to be a rest controller I need a class that I can go ahead and test I want to be able to get to this endpoint but I want it to be secure so let's talk about this so I'm going to create a new git mapping in springboot 3 I need to go ahead and be explicit about that so I'm going to say git mapping forward slash I'm going to say I want this to return a string and I'm going to eventually get the principal so I want to actually I want to get to the Authentication so from Spring Security core Authentication and what I can do is just return a message that says hello and then we'll use the authenticated principles get name so once I have that this is what I want to secure right we need to start somewhere we need to secure this the way that I'm going to do that is I'm going to create a new package and I'm going to create a package called config now the reason we're doing this is if we go ahead and start this application up now by default because we have Spring Security on the class path because we have not overridden the default security out of the box we are getting some defaults for us right everything is locked down by default which is a good thing we also have a default user with a username of user and a password that is randomly generated now this is great for just kind of testing things out but what I want to do is set up my own security config so to do so I'm going to create a new Java class and we're going to call this security config and inside of here this is going to be a configuration class and we are going to enable web security the first thing I'm going to do is I want to create a user that I can use in this system to test out so we are going to start with by creating an in-memory user user now I get this question all the time and I understand the frustration what Dan why do you use an in-memory user why do you use HTTP basic and the reason for that is to keep these tutorials nice and concise if we did all of the things that everybody needed to do in some of these security applications these tutorials would be really long and nobody would watch them so what I'm saying here is we're using an in-memory user but I have other tutorials you can find other tutorials on YouTube or on the web on how to replace that in-memory user with something you grab from a database so I just need a user to test out here so I'm going to create a new beam this is going to be of type user details service and we'll call this user details service and all we're going to do here is return a new in-memory user details manager and what we're going to do is we're going to call user dot with username so the username that I want for this is going to be D Vega we're going to set a password so we need to figure out how are we going to do encoding on the password we need to we normally don't want to store plain text passwords right in this case we don't care about that because it's an in-memory user so you could set up a password encoder or right here in the string you can just use these curly brackets and then Define the encoder in this case I'm saying I don't want a password encoder just use the plain text of password so now I have a password I also need to set some authorities so this user is going to have an authority of read and a role of user we'll talk about more while we have both of those there in a little bit and finally I'm just going to build this so because I'm going to build this this is going to whoops uh nope we want to let's just do this right here and return that so now I have a user good off to a good start I have a user in the system now I need to override that security configuration I've done other videos on this in the past we used to go up here and our security config and extend something called the web security uh configure adapter rolls right off the tongue right so you see that that's not even an option anymore as of spring boot 3 that has been deprecated and removed so you can even do that anymore and what we knew what we do now is we've moved to a more component based model of this so the way that we're going to do this is we're going to define a new Bean because again this is a configuration class and we want to return a bean of type security filter chain so we're going to say security filter chain and we'll call this security filter chain and this takes an argument of HTTP security there we go we'll call this HTTP and we're just going to return whatever the HTTP build method returns and this is going to throw an exception so now that we have that we can start to Define our security config and what is this going to look like so first off I'm going to go ahead and disable csrf so we're going to say csrf abstract abstract HTTP configure that's the one I wanted and we're just going to disable that and then I'm going to use authorize HTTP requests so again we might have used authorized requests in the past now we're on Spring boot 3 spring framework Spring Security Six in Spring Security Six authorized request has been deprecated we want to go ahead and move to authorize HTTP requests so we're going to get off here and we're gonna say auth and then I want to go ahead and say any request that comes in you must have the authority of scope read so we'll talk more about that in a second but we're basically saying no matter what the request is somebody who if you're trying to hit an endpoint in our system you need to have this Authority otherwise we are going to turn you away you're not you're going to be unauthorized to access that so so far so good I'm also going to set up a session session management here because we don't need sessions so I'm going to say session dot create a session policy a session creation policy we're gonna say this is stateless so we're going to pass that in um then I'm going to go ahead and set up my oauth to Resource server so we'll talk about this in a second again I've covered this a little bit in more detail in the previous tutorial that I mentioned uh at the open of this and also in the GitHub repository so if you want to kind of dive into this a little bit more you can but all we're doing here is we are saying that we are going to use the built-in JWT support I need a way to log in I need an authentication mechanism again I'm going to use HTTP basic here so if you don't want to use basic I've got other videos on how to kind of move away from this and use something like a username and password authentication with say an endpoint again I'm trying to keep this as concise as I can so that's what we're going to do here so with all of that we should be good on the security filter chain declaration here we have our security we're saying hey go ahead and any request uh you will turn them away if they don't have that specific Authority so we know that our D Vega and our password username user here does have that Authority that scope so that's good we're also using HTTP basic and then we've set up the oauth to Resource server so now with this HTTP basic when I log in I'm going to basically log in with d Vega and password and I'm going to get a roll of users so at some point once I'm logged in once I'm authenticated using HTTP basic I want to be able to get a JWT token right so we need to create a way to get a token so let's pretend we have a token controller so let's go in here we'll create a new Java class we'll call this auth controller I'm going to create a rest controller here this is going to have a request mapping of Slash API slash auth and then inside of here we may have a post mapping where you say to slash token I'm going to pass in some authentication and I'm going to get a token back so authentication authentication go ahead and return to me some kind of token right so the next step is okay we need a way to generate this token but we kind of skipped something here when we Define this uh oauth2 resource server in here let's go ahead and download this it's basically saying that when we do this we need to Define um where are we so when we are in the JWT customizer uh when we're in here we need to so this enables our JWT encoded bear token support so now we get this JWT support out of the box which is great but when we do that when we use the jw2 customizer we need to do one of the following and one of the following is expose a JWT decoder beam so we need an encoder and a do-code decoder so we talked about in the readme we talked about the fact that we are using symmetric key encryption this means that the same key is going to be used to sign the token and the same key is going to be used to verify the token so we have one key used for both so what is that key we actually need to set that up right so we need a JWT key let's see right here if this is the one I was looking for yep so I'm going to set a property called JWT dot key this key can be just a random string if you wanted to at any case for the example that we're working on it must be at least 256 bits so I generated this random 256 bit key but you can just put out you know if you just want to write in some random string you can just make sure it's 256 bits so we have this key in here we want to be able to go ahead and pull that into our security config so right at the top of my security config I'm going to go ahead and say private string uh JWT key and we'll go ahead and get this using the add value annotation and so in at Value will reference this by saying GWT dot key and we'll save that and we should be good to go there so now that we have this in place we can create an encoder and a decoder so a way to basically encode the signature of the JWT and then a way to verify that signature of the JWT so I have a couple shortcuts here we don't want to watch me kind of type this out so I have an encoder and a decoder so the encoder is going to return that Nimbus JWT encoder which is a part of the resource server when we bring in kind of like uh the Jose stuff we get all this part of that so now in a previous video we used something different when we use like a public key right we had a public key and a private key here we're saying um instead of a public or a private key you're saying hey I'm gonna have a new immutable secret and we're going to pass in that key and it actually needs to take in a byte array so we're just going to turn that string into our byte array by calling get bytes so now we have an encoder now we need a decoder and this is where some things get a little tricky so I have to create a new secret key spec so this is part of the Java x.crypto.spec so this was this some of this stuff is kind of new to me uh maybe it's not new to you but this is where this was a little tricky for me so you have to set up a secret key spec and with that secret key spec now you can go ahead and pass that into with secret key it's not as easy as just saying hey let me pass in a string you need to set up a secret key spec then we need to define the algorithm that we're using in this case I want to use the same algorithm that I'm going to sign with as the same algorithm I'm going to decode with so we haven't signed our JWT yet this is just the encoder that we're using when we go ahead and create a token service to sign that signature we will go ahead and use this same Mac algorithm so we're using Mac algorithm hs512 so that is the encoder in the decoder so we're almost there with that in place we can go ahead and create a new package we'll call this service I'm going to create a new Java class called token service and inside of this I'm going to say this is a service and inside of here I'm going to get an instance of that JWT encoder because again if we go back to here this is a bean so we know that it is now in the application context so I can ask for it in a component like any other class so I can say private final JWT encoder JWT encoder actually let's just call this encoder and we can get this through Constructor injection so once I have the encoder I can create a method that will generate the token so we're just going to kind of put some code in here so we don't have to walk through all of this let's go ahead and see if we can't get these import words in here okay so now we have these in here let's take a look at this code and find out what's going on um what we are doing here is we are getting an instant for now we are setting the scope so the scope is for the permissions like the authorities that we care about with the user so what we're going to do is we're going to map whatever is coming in to a granted Authority now we're not mapping anything that starts with roles so we're kind of filtering those out and the reason for this is when somebody logs in with HTTP basic they're assign this role of role of user and we don't care about that what we care about is the authority of scope because we want to assign that user this Authority and that is going to be what's in the JWT token right so we want whatever's in the JWT token the scope of read that's what we're checking for in our Spring Security config when we're saying hey go ahead and lock down any requests and make sure they have an authority of scope read Because if we just said user roll our roll user that they get that when they log in with HTTP basic so then the JWT is really kind of pointless right so what we're doing is checking that JWT for a scope of read so we're doing that we're collecting that we're setting up a claim set so the issuer itself I'm self-issuing this uh it's issued at now it's going to expire one hour from now here is the subject here is the claim that is the scope that we've created go ahead and build that finally in a previous version we just kind of passed the encoder and return the token value but we need to set up some encoding parameters when we're changing the algorithm that we're using so I set up these encoded parameters so we're seeing jws header with Mac algorithm hs512 to match the one we are decoding with we pass that into our encode method and that returns a token so so far so good now the only other thing here is that we do have this auth controller right and we need to return the token so we need to get an instance of that token service so I'm going to say uh private final token service token service and we'll get that through Constructor injection and then what I'll say is token service dot generate token and we'll pass in our authentication all right so with that in place we have this last endpoint that we need to do a little configuration for so if I go into here and I say um I want to go ahead and use request matchers and say anything to slash API slash auth slash token to go ahead and access that you must have the role of user and so now we know that this particular user this in-memory user has a role of user so when you log in Via HTTP basic you'll have this role of users so you'll have access to it and then once you get here we have our authentication we are now authenticated via HTTP basic we have that authentication we can pass that to the generate token method we use that git name which in this case is D Vega and we build out our JWT token so I think we are good let's just double check everything we have so let's talk through this we have our security config we have a home controller so if we access uh localhost 8080 when this starts up we should get it unauthorized we have another controller endpoint that allows us to get a token but we can't get a token unless we pass in some basic auth parameters because that's what we're using here once we do that we should get back a JWT token because we are generating that using the token service and uh we have our encoder and our decoder here we're using the built-in oauth 2 resource server along with uh the JWT so that should just go ahead and check that JWT token and allow us access so I think that everything should work let's go ahead and cross our fingers we're going to restart this application and it looks like that started up so let's do this let's head over to postman and see if we can test this out all right so I'm in Postman I'm gonna make a get request to you localhost 8080 I have nothing in the authorization so normally we'd pass a bear token but we don't have one yet so this is the first run I'm gonna go ahead and hit send and yes we are indeed getting a 401 on authorized so now what we want to do is we want to create a post request to localhost 8080 API auth token we need to pass in our user credentials this is really easy in Postman if you go over to authorization select basic auth and then choose your username and password let's go ahead and put in a bad password first so just something that doesn't work and go ahead and click Send and we are still getting a unauthorized so now if we go ahead and put in a correct password and click Send we are indeed getting the token back that we expected to get back now that we have that token we can go over to our get request and choose authentic authorization type Bearer token because again that's what the oauth resource server JWT setup we did in the security config enables us to do it gives us that JWT bare token support out of the box so now we're passing in an authorization of bear token here's my token go ahead and click Send and indeed we're getting the the welcome message hello and then the username and we have a 200. so wow that was a lot I know that was a lot there's a lot of steps in this but I wanted to kind of uh I didn't want to drag this out I wanted us to get through this pretty quickly and at least see an example of this so this is really in contrast to what we talked about in a previous tutorial with asymmetric key encryption we have a public and a private key now those are great for certain scenarios there's a little bit more work involved right like you need to understand how to generate those public and private key pairs you need to know that that private key should not be checked in to get and stored anywhere right in this case I have the same key that I just have in my application.properties and that I'm using to go ahead and sign the signature and verify the signature as well so again this could be a value you know so this is an example that I'm using in Dev so in a production real world scenario this may be some type of key that I rotate so you could use some type of a different way to provide this value you know this may come through some type of environment variable or configuration or through some like Secrets manager depending on you know how you're kind of deploy into production so cool I think that's it uh I really hope you enjoyed this tutorial if you found some value in this do me a big favor friends go ahead and click that thumbs up subscribe to the channel and as always happy coding [Music]
Info
Channel: Dan Vega
Views: 2,184
Rating: undefined out of 5
Keywords: dan vega, spring security, spring security tutorial, spring security jwt, spring security jwt authentication, spring security oauth2, spring framework, spring boot, spring boot security, spring boot rest api
Id: 66DtzkhBlSA
Channel Id: undefined
Length: 27min 53sec (1673 seconds)
Published: Thu Jan 19 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.