Spring Security JWT: How to secure your Spring Boot REST APIs with JSON Web Tokens

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
if you perform a quick search on how to secure your rest apis in spring boot with json web tokens you'll get a lot of the same results these results contain a method that involve writing a custom filter chain and pulling in a third-party library to encode and decode jwts after staring at these tutorials confused more than ever i thought to myself there has to be a better way to do this so i did what anyone with access to the spring security team would do i asked them sure enough they informed me that spring security has built-in support for jwts using oauth2 resource server in this tutorial you're going to learn how to secure your rest apis using jwts and spring security welcome back everyone my name is dan vega and i'm a spring developer advocate for vmware it's my mission to help you learn how to build and deploy applications through a series of tutorials like the one you're going to watch today i'm really excited for you to go through this tutorial it's been one of my most requested videos and i spent a lot of time putting some thought into what someone would need to know to get up and running with securing rest apis with jwts and i have built an article to go along with this video that has a little bit more in-depth uh walkthrough of things like the application architecture that we're going to go through that we just can't cover in this video it's already like 45 minutes long and so if you want a little bit more detail i would suggest checking out this article i'll leave a link to that in the description below again this is really the starting line for securing your rest apis with jwts not the finish line i know there are more things that we can do but i wanted to give you the bare minimum the bare bones that would help you get up and running so what i'd like from you is to go through the tutorial if you have questions please let me know leave them in the comments below reach out to me and we'll see if we can't continue this uh journey of securing your rest apis with jwts in another video with that i hope you really enjoy this let's get right into it we will begin our new project like we do every project over at start.spring.io so we're going to fill in some metadata here we're going to choose maven java we're going to use the latest stable release which at the time of this recording is 2.7.3 we're going to fill in some metadata so i'm going to say my group is dev.dan vega my arti artifact is going to be jwt demo you can fill in whatever you want here packaging jar java we're going to use java 17 and next we need to go ahead and select our dependencies so i'm going to be building out a web app so i'm going to choose spring web we also want to go ahead and bring in the oauth 2 resource server and finally we're going to use the configuration processor to create some configuration properties so those are the only dependencies we're going to need now i know you're probably thinking to yourself dan where is spring security and if you dive into the oauth 2 resource server dependency you'll see that it actually includes a lot of the spring security stuff that we're going to need and we'll take a look at that in in a second so right now just go ahead and generate that project i'm going to download that and open that up in my favorite ide intellij you can open it up in whatever ide or text editor you're most comfortable using i have the project open in intellij and the first thing i want to take a look at is if we open up that palm.xml i just want to refer to something i talked about a minute ago which is the spring boot starter ol2 resource server dependency if we dive into that we're going to see a lot of the dependencies that we need for spring security so we see spring security config spring security core we see the os 2 resource server and also spring security oauth 2 jose which includes some of the stuff we'll use later like the nimbus jose jwt library so we have everything we need to get started the first thing that we're going to need is a rest api that we want to go ahead and secure i think for demo purposes we're going to go ahead and keep it simple here we'll just create a home controller in a controller package with a single method that returns a string so what i want to do is go ahead and create a new package we'll call this controller i'm going to create a new class called home controller and this is going to be a rest controller and if we go ahead and create a get mapping so this will be the route we're going to create a method that is going to return a string called home and for now we'll just return hello gwt later on we'll switch this to return the authenticated user's username so with that in place i can go ahead and run the application to make sure that it's working okay and it does if we go to localhost 8080 it will now redirect us to a login page because uh everything is secure by default that's spring security takes that kind of secure by default approach which means that everything is locked down and you are going to have to open things up as you wish as opposed to kind of leaving everything open open and then you having to lock things down so um that is kind of the the start of it so by default spring security is going to set up a default user name user with a password that is generated for you here in the console but it is our job that at some point the default security configuration is not going to be enough to get us up and running right it's good for demo purposes but at some point we're going to need to provide our own configuration to fit the needs of our application so in the past you would have to extend something called a web security configure adapter this has since been deprecated in spring security 5.7 if you're interested in learning more about that i have a video on that i will go ahead and link to in the description below and and the card up above so if you want to go ahead and pause this one and watch that one to kind of gets uh get up to date on on the security changes go ahead and do that what i'm going to do though is i'm going to create a new package here we're going to call this config this is where all my configuration classes are going to go i'm going to create a new class and we're going to call it security config again this is really up to you we're going to go ahead and annotate this with ad configuration because we're going to define some beans in here and we're also going to uh enable web security so the first thing we're going to create in here is a bean of type security filter chain call the security filter chain this actually takes in http security we'll call it http and at the end of the day what we want to do is we want to return http dot build and that is what we will return and this will throw an exception and so here's where we get to really start to customize our http uh security so the first thing i'm going to do is uh disable csrf cross site request forgery so cri csrf we'll go ahead and say csrf.disable and then we're going to set up our authorized requests so this is going to say what do we want to do here by default i just want to say any request i want you to go ahead and be authenticated that is going to be kind of the default for us um going forward again because we've you know by default spring security or swing boot did that it kind of locked everything down right but we have overridden the default security so now we have got to be responsible for anything that we're doing here so we want to say go ahead and um authenticate anything so the other thing we're going to do is we're going to turn off session management we're going to create like a stateless session because we are just kind of using rest apis here we don't need a session so i'm going to say session create policy we want stateless and that is that uh finally i need a way to authenticate users uh you could use like a form login like the default security configuration had we're gonna use http basic here again it's easy to get up and running and for this purpose it's great so i'm going to say customizer.with defaults and i'm going to actually import that as a static import so we just have with defaults and build so that is looking good let me go ahead and shorten that a little so so far so good right um just a quick tip uh never disable csrf protection while leaving session management enabled doing so will open you up to a csrf attack so just an important note there not in this app but in any of your other apps so now that we have our custom security in place um you need a user that isn't that default one right i want to go ahead and create a new user for this system and to do so i'm going to create a new bean we're going to say public in memory user details manager we'll call it user and we're going to return an in-memory user details manager and in here we'll go ahead and create a new user with username dvega dot password so i need to encode this password i could create it like a no op password encoder bean but you got some do that right here in the password just by saying no op and password and then i also want to set authorities which we won't really get into in this particular tutorial but could be useful later downline and we'll go ahead and build that and i probably missed something user oh we need a new that would help all right so that will give us a default user in the system um with that we should be able to go ahead and save that restart stop and rerun and we see that in the console we no longer get that generated password for the default user uh so now if i go over to localhost 8080 we go ahead and we get jwt i wonder if it's picking up a prior let's just see so yeah i was must have been picking up a prior login so if i just type in anything it's not going to let us but if i go ahead and use d vega and password all right we are able to authenticate and we are able to see our jwt hello jwt so with that in place i'm just going to change one little small thing at some point we want to display the authenticated user's username so i'm going to go ahead and say principle.getname let's go ahead and restart this and we run this and there is our username i just want to take a quick break to say this is a good time to head back over to the article and dive into the section about oauth 2 resource server so in there we mentioned that hey there's spring security supports protecting endpoints using two forms of oauth 2 bear tokens and that's gwt and opaque tokens so again we're jumping into jwts this is really handy when you want to delegate authority management to some type of authorization server so now we've introduced a couple terms here right resource server and authorization server normally in a larger scale distributed application you would have an authorization server that handles this and that's a separate service today we are using self-signed jwts and this will eliminate the need to introduce an authorization server so we don't need that today this works for this example but as your application requirements might be different or as they grow the question that's going to come up is when is this no longer acceptable when when can i move when do i need to move away from self-signed gw2s and i got two really good answers from the spring security team and this was just really quick so i'm sure they have better in-depth answers but i thought they're really like concise and right to the point so i want to share those with you again these are in the article when you reach the point where the trade-offs for self-signed gwts are no longer acceptable an example might be the moment you want to introduce refresh tokens so if you need refresh tokens in your application maybe you need to move to a authorization server for that the other one was i'd add that a distinct authorization server makes more sense when you have more than one service or you want to be able to harden security isolating something as critical as authentication provides value because the attack service is reduced and i again i thought that was a really great answers you know in a distributed architecture you wouldn't want to recreate the authorization mechanism multiple times right so you can just have one authorization server and we're going to be doing more tutorials on this spring authorization server is coming up on 1.0 milestone so we'll start talking about that more if you have questions about that please let me know i've given you some resources in the article to talk about resource server for the documentation hey what is resource server with jwts mean and hey what is this spring authorization server i do need to move to something like that what is it how can it help me so i just wanted to take a quick break to talk about what a resource server is what an authorization server is because these are terms that you're going to be hearing but with that let's jump back into the tutorial and start writing some code all right now that you know what a resource server is and what it's used for we need to configure one so you can do this right in the security config by setting the oauth2 resource server option here so we're going to say oauth oh auth 2 resource server and then what we need to do is pass in a configure so we could create our own but luckily for us we don't have to there is a class called the oauth2 resource server configurer so oauth oauth2 resource configure so if we jump into this we can take a look at what this is so this is an abstract configure used by the resource server by default this wire isn't wires in a bare token authentication filter which we'll need which can be used to parse requests for bearer tokens and make an authentication attempt so we have some options here of what we can configure what we want to use is the jwt customizer so this enables jwt encoded bare token support so that's what we're looking for so we can do that with a method reference here and just say jwt so that is going to give us exactly what we want now i want to look at this class one more time because if you look up here if we use the jwt customizer we need to do one of the following we need to either supply jwk set uri a jwt can decoder instance via this or we need to expose a jw t decoder bean and the reason is if you go ahead and restart this we're going to get an error and it is saying hey um in that set filter chains we need a we need something called a jwt decoder and you have not defined one so that is what we need to do next all right so we need to talk about signing json web tokens and to do so i'm going to go back to the article that i've written to accompany this tutorial so we know we need to create a jwt decoder but there's some things that we need to do here as we learned earlier there are three parts to gwt the header the payload and the signature that signature is created by encrypting the header and payload or encoding the header and payload with a secret or by signing it with a private key so the definitions are here on screen you can pause the video if you want but you can either use a symmetric key which is a shared secret so just like a secret key or asymmetric keys which is the use of a private key of you know a private and public pair so that's symmetric versus asymmetric there are pros and cons to each but it's generally recommended that you use asymmetric keys so that's the approach that we're going to take here so what we need to do is we need to create a public private keypair this is something you can do via code and it might make sense to do it but for now we're just going to create them manually statically here and i think it'll just kind of give you a window into what we're doing so i'm going to create these in a new folder under source main resources and we're going to create a new folder here called certs again you can really call it whatever you want so i'm going to call it certs and so what i need to do is actually open up a terminal to that location so i'm going to go into source main resources certs and now that we're there let's go ahead and make this a little bigger and clear that up so we're going to run a few commands again we're going to use something called open ssl which is installed by default on mac os you should be able to get it on whatever operating system you're running normally i think we can get away with two commands but i'll talk through the reason for the three commands here i'm not an open ssl expert i'm not a rsa token expert so if somebody wants to call me out on that in the comments please do so and we'll get that fixed up in the article but what i do here is i'm going to run open ssl and what we want to do is we're generating an rsa token or an rsa key so we're going to say rsa gen rsa the out is going to be named keypair.pem and then you're going to use the kind of encryption level i'm just going to say 2048 this is kind of the min standard you can use all the way up you know there's different options for that so i'm going to go ahead and do that and we see we generated an rsa private key so this is generating the private key i know i called it key pair but i'll tell you why in a second so from the private key we can extract a public key so the way that we do that is we say open ssl rsa and then the in is going to be the keypair.pm that we just created and what we want to do is we want to get a public out and the out file is going to be called public dot pm so if we run everything okay uh we missed something here let's see uh rsa not rs let's fix that writing rsa key so now if we look in here we have a public key this is a public key now if we were to just call this private and run with it at the end of the day we would end up getting an error in our application saying that the private key needs to be in a pm encoded pkcs8 format so what i'm going to do is make sure that we have a private key in that format to do so i'm going to run open ssl pkcs 8 top k8 inform so inform pem so out form is going to be pem no crypt in and then the in file is going to be the keypair.pm and the out is going to be private.pm so if everything works we should have a private so we have a private key and now we have a public key and those are in the formats that we need at the end of the day if everything worked you can get rid of this all we really need is the public and the private key all right so we have our public and private key what i want to do now is create some configuration so that we can configure where the private and public key are coming from we want to externalize this so eventually if this were to go to production private that private key that we have in our search folder is not something we would want to commit to get right this is something we'd want to store privately we can externalize that configuration and then load it maybe from like an environment variable in some other environment so to do so that is why at the beginning of this tutorial we brought in the spring boot configuration processor what we're going to do is create in our config package a new record so we're going to say java class this is going to be rsa key properties and it's going to be of type record we're going to mark this with add configuration properties will have a prefix of rsa and that is that so this is the hide notification we know that we need to go ahead and say let's just do prefix um and then to make this happen i'm gonna go over to here and say enable configuration properties rsa key properties dot class that should be that and that is that so this is really just going to take in uh two arguments here so they're gonna both be or actually they're gonna be different types so we're gonna have rsa public key and this is gonna be public key and then rsa private key this is going to be private key so that's all we need to do for this class now what we can do is go ahead and set those values so i'm going to come over to application.properties i'm going to say rsa now again they're not going to be available yet and this is just something i've covered in other videos but once we made that change we added that configuration properties we do need to run this build and then you'll see them show up down in our target folder and now as we start to type we will get that intellisense so i'm going to say rsa private key this is going to be a so class path oops class path and this is going to be inserts and private.p.e.m our say.public key is class path um certs.public dot p.e.m okay so that looks pretty good now what we can do is um we can go back to our security config and we can refocus back on the jwt decoder so remember i said one of those things that we have to do is we can define a jwt decoder beam and the way that we do that is we just say app bean here in our spring security config and this is going to be of type jwt decoder gwt decoder and we need to return some type of jwt decoder so back when we took a look at some of the uh dependencies that the oauth2 resource server brought in one of those was the nimbus jwt or nimbus data t jose i think it is uh or spring security oauth 2 jose which brings in nimbus jose gwt at the end of the day we get something called a nimbus jwt decoder and this class has a bunch of useful methods in here and one of which is with public keys so we know that again there are two different ways we can have symmetric or asymmetric if you're using symmetric and you're just using a secret key you could use with seeker key we are using that public private key so we're saying with a public key i want you to go ahead and decode that so now we need to find out where that public key is right and the way that we can do that is we can get a reference to that rsa key properties class that we have so we could say private final rsa key properties i say keep let's just say rsa keys keep it simple and we'll get that through a constructor injection so now down here i can say rsa keys dot public key and we want to go ahead and build that and that will return a jwt decoder so remember we started the application up earlier without this being and we got that error what i want to make sure now is that if we've done everything right we can start up the application and there is no error so that's telling me two things we've correctly configured this jwt decoder bean and we've also uh have the correct path set in application.properties to those files or we would get something drawn there as well you have the keys in place and you've defined a decoder which is a way to decipher that gwt if you remember back to our architecture diagrams earlier in the article the user will need to log in with their username and password so we're using basic authentication here and if they pass authentication we need to generate a new json web token and send it back in the response so to make this happen the first thing that we're going to do is back in our security config we're going to create a bean of type jwt encoder so we have the decoder we need an encoder so let's go ahead and say bean jwt encoder gwt encoder and what this is going to return is a j uh a new so again we use nimbus for the decoder nimbus is the library is here we have a nimbus jwt encoder what this is going to take is a jwk source so we need a way of creating that so we're going to start off with creating jwk gwk is equal to new rsa key dot builder and this is going to take in our public keys first so we're going to say rsa keys dot public key and we're also going to pass in our private key and we're going to go ahead and build that so once we have the jwk we can create a jwk source this is security context and we'll call this jwks and we're going to use the immutable jwk set which is going to take a jwk set and we can just pass in that jwk we defined above so with jwks we can pass this in as an argument and that should give us everything we need to create an encoder so we could create a controller now and we can use this encoder directly in our in in say our authentication controller but i feel like this is something that we should extract out to a service layer so what i want to do here is i'm going to create a new package here we're going to call this service and in here i'm going to create a new class and we're going to call this token service and in token service is going to be of type service again if you're new around here this could be at component this is a specialized annotation of component but this will just tell tell spring hey this is a class that i need you to go ahead and manage for me create it stick it in the application context so i can use it later so now that i have this token service i need that jw t encoder so i'm going to say private final jwt gwt encoder here's our encoder we'll go ahead and get that through constructor injection now i need a way to generate the token i'm going to go ahead and just throw some code in here and we'll talk about it let's import the security one and uh let's see why is this not why did we not call this encoder that would have been better let's just do that and that okay so this generate token method is going to take in uh authentication so this will be authenticated user it's going to create an instant for now get a timestamp basically it needs to create a scope so the scope is a string so it streams over the authorities maps them to a granted authority and just creates a string based off of those and then this is the important part the jwt claim set so this is we're saying the issuer itself remember we're self signing these jwts when was it issued at when does it expire i'm saying now plus one chrono unit of hours so this will expire in an hour if you want to go ahead and change that unit to be whatever to fit your needs please do that the subject is the authentication that's the principle that get name the claim is the scope and then we pass in the scope that we created up here and then we go ahead and build that jwt claim set then we can use the encoder to encode that so we're we're calling in code method from the encoder and using the jwt encoder parameters from our claims and then we get that token value so that at the end of the day returns a string that generated token that we need so now that we have that now we can kind of start to fill this in kind of one of the last pieces here which is we need a controller we're going to call this auth controller and this is going to be a rest controller and so the first thing i'm going to do is i'm going to get a logger in here so i can go ahead why didn't i change my live template to do that and what i also need is a token service that we just created so token service and we will go ahead and get that through constructor injection and what i'm going to do is create a method called token which is going to return some kind of string so this is going to be a post mapping not a post construct and we're going to map this to slash token so this is the end point that they're going to hit they need to pass in some type of authentication we know that's going to be basic auth because that's what we set up and what i want to do is just go ahead and do some logging in here so i'm going to say log debug token requested for user and we'll say that placeholder and then we'll say authentication.getname [Music] did we pull in the wrong authentication i keep doing this yes we did not want that one we wanted the spring security core and we can get name from that so we want to get the token now so we're going to say string token is equal to our token service dot generate token we're going to pass in our authentication and that should give us what we need so i'm just debugging this for you know development purposes so i can see who requested one uh what the token was generated so i'm going to say token granted and we'll just go ahead and pass in the token there so at the end of the day we're going to actually return that token and if everything was done correctly we should be able to restart our application without any errors oh and we did it you know the demo gods are smiling upon us today my friends so that's everything should be working right but we need a way to verify that it's working all right so there are a couple ways that we could manually test this but in this tutorial i just want to show you two quick ones the first one and the easiest way is to probably use a tool like postman so if we head over to postman we can create a get request to localhost 8080. again everything is locked down right now so if we go ahead and send this request we are going to get a 401 unauthorized we need to log in first we are not authenticated to view uh this localhost 8080. so what we can do is we can send a post request to locohost8080 slash token again that is what we created here so slash token this will take in some authentication pass it to our generate token method and then create a token this is just realize there's a typo here so this should give us everything we need let me just restart that okay so far so good so let's go over here and go to locos 8080 token and now we need to pass in our user credentials so the way that we do that here in postman is through authorization and we are using uh basic all so i'm going to pass in our d vega and password and if i go ahead and send this here is that jwt token now what we can do with this is take this over to this other request localhost 8080 and in the authorization tab we can pass different types of auth here one is a bearer token and so this is that token that we got back it's called a bearer token and we pass it along with the request now if we make this request we go ahead and we hit the endpoint we get a 200 okay it says hello and if you remember we added the authenticated user in as an argument so we're able to get the authenticated user's username there so that's one way to do it using postman another way that we can do it is by popping open a terminal here so i'm going to clear this out and i have a little application that i love called httpi and it just makes doing running commands from the command line especially testing out apis makes it a little bit easier so what i can do is i can say http post so i'm running a post request to slash 8080 and slash token and i'm passing in some auth which is d vega and password and if i run use the dash v argument i get i get to see both the request so the request sent and you can see we're sending an authorization http basic and here is the base64 encoded username colon password so what we get back is a success so we get this token back right so now we need a way to go ahead and pass that to localhost 8080. and we can do that so we can just say http let's come back http 8080 and i'm pat passing in the authorization header so the way that this works is it has a bearer the keyword bearer and then the jwt token and we want to go ahead and close that there and if you see that it ran uh successfully so two easy ways to go ahead and manually test it using both postman and going ahead from the command line now again i'm using a utility program called httpi you can do this with curl as well well it's just a little more verbose and i never remember the commands do that stuff so that's why i use that program all right so manual testing is great but you can see everything is working as it's intended however we're going to need some automated tests in place so that as you make changes you can be confident that what you're introducing is not breaking existing functionality so i'm not going to go into this too much this tutorial has gotten pretty long as it is but i just want to provide you with a simple example of how to write such tests before we can do so you have to go into the palm.xml and we can see down here we have this starter test when we brought in that oauth 2 resource server one thing that didn't get brought in was the spring security test library that we need so i'm going to say spring security test and that's from spring security so let's go ahead and reload our changes and everything is okay i'm going to close that i'm going to go into my home controller and i'm going to create a test called home controller test and let's just go ahead and make this easy and put in some code here so we are doing a couple things um we're gonna have to bring in all these i'll just do these as we go so we are creating a slice test and we are specifically focused in on the home controller and the auth test because of that we are doing a slice test we're not going to load everything into the application context we need to import things that we're going to use like the spring security config and the token service because it's a slice test we get a auto wired instance of our mac mvc and then the test is hey when it's the route when it's unauthorized then hey we should get a 40401 um mbc let me just go ahead and add these in so we don't have to go through and add all of these um is that not in right [Music] and that one okay now we should be okay so um that is that one then we have a root when authenticated then says hello user so i'm actually just showing you two approaches to this one we're gonna make a post to token with our http basic username and password that should be okay and it should return something from that result we're going to actually get the response so we can get the token then make a request to our route with the authorization header with that bearer token and we expect what we get back should say hello and then my username is d vega also just a simple test here you can use this at with mock user annotation so now what we're saying is hey we just want to make sure that we get a 200 back and that we're not getting a 401 bag so let's go ahead and run this and great all test pass now we have our automated tests in place now in a real world application there would be more tests here right but i just wanted to show an example of how you might test this out because we do want these automated tests in place again it just kind of gives us the confidence as we're making changes that we're not introducing any new bugs i really hope you enjoyed that tutorial when i sat down i had really created a goal for myself to create a tutorial through article through a video to let everyone know that there was an easier way to secure your rest apis with and that spring security has this support built in so i hope i've accomplished that goal i did for me you know again that was one of my one of my passions here is learning and then teaching that to you guys this is something i needed to learn too so it was fun kind of going down this rabbit hole and understanding what spring security has to offer out of the box so i hope you had some fun learning with me uh also i just wanna i said this in an article but i feel very fortunate to work for a company like vmware to have access to the resources that i have access to which is thing you know teams like the spring security team they were all very helpful not only extremely smart but everybody's just very open to sharing knowledge and now i know you don't have access to every single engineer at vmware but you do have access to me so if you have questions the way that i have questions please reach out to me let me know what they are and i'll see if i can go ahead and help you out so as always friends if you found value in this please give me that thumbs up that really helps me out subscribe to the channel and as always happy coding we go [Music]
Info
Channel: Dan Vega
Views: 38,632
Rating: undefined out of 5
Keywords: dan vega, spring security jwt, spring security, spring boot security, spring boot, jwt, spring security tutorial, spring security jwt example, java, spring, authentication, spring boot security introduction, authorization, spring security jwt authentication, security, json web token, secure spring, java security
Id: KYNR5js2cXE
Channel Id: undefined
Length: 43min 6sec (2586 seconds)
Published: Fri Sep 09 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.