Spring Security JPA Authentication in Spring Boot

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
if you're working in a spring boot application that uses Spring Security and you need to authenticate against the database using spring data jpa you're in the right place welcome back everyone my name is Dan Vega and I'm a spring developer advocate for VMware so we've been doing a lot of videos on Spring Security lately and we've taken a natural progression to working with users so we started out with creating an in-memory user which is really great for demos prototypes proof of Concepts not really great for production right we moved on to using jdbc so we talked to a database using jdbc there's actually a built-in mechanism for this in Spring Security called the jdbc user details manager so we look at we looked at both of those but there is no built-in mechanism for talking to a database using jpa that's the bad news the good news is it's actually pretty simple to do once we understand the different components that we need to work with so that's what we're going to do today we're going to build an application from scratch using spring boot spring data jpa will use an in-memory database to start again this can be kind of moved over to a real database later on if you want but we'll talk about the different pieces that go into building authentication talking to a database using spring data jpa I've been looking forward to this one I hope you enjoy it what are we waiting for let's write some code all right so we're going to get started as we always do over at start.spring.io this is a Java project using the latest stable version of 2.7.4 we're going to go ahead and enter a group of dev.dan Vega we'll call this jpa security as always the code that we go through today will be in the description below so go ahead and grab that if you want to follow along I'm going to go ahead and add some dependencies here we're going to build a web application we're actually going to use jpa as well so spring data jpa we also need a database I'm going to choose H2 I hear some people moaning um don't worry about it I know it's an in-memory database not for production this nothing changes if you swap in something like MySQL or postgres or SQL Server if you want a another example of me using something like postgres with like a Docker compose file please reach out to me let me know in the comments and and I'll put that together for you so we have spring web spring data jpa our H2 database we're going to go ahead and generate this and open it up in your favorite IDE I'm going to go ahead and open this up in IntelliJ and we'll get started all right so I'm going to get started by creating a few packages in here just we kind of have a setup so I'm going to need a config package I'm going to need another package for controllers I'm going to need a package for let's say a model we'll also want a package for a repository so repository I don't know why I always have trouble spelling that I don't know what is going on there um so we have a repository we have a model we have a controller we'll probably have a package for a service so I think that will be enough uh the first thing that we need to do is we need to create a model so we're creating a entity for posts so I'm working with a post domain we're building out a Blog here I have a post that has things like title content author Etc so I'm going to go ahead and create a new post so we'll call this post and in the jpa world we need to mark this with at entity I don't want to get two specifics into kind of spring data jpa this is more focused on the security stuff so if you're interested in more content around GPA let me know so we need an ID and we need to generate that value because I don't want to do it so we'll say private long ID we're also going to create a title we're going to create a slug we're going to create some content we're going to create an author we'll have local date time for published on and then we'll have one for uh not created on we want updated on so that gives us everything we need we also need a no args Constructor because we are going to overwrite the implicit one because we are going to create one for going ahead and setting a new post so I'm going to set a new post I'm going to take in a title a slug and some content and an author so that's all I'm going to take in the reason for that is so let's do this looks pretty good wow thank you GitHub Copa it just keeps saving me time here so we're going to set the title we're going to set the slug we'll set the content in the author the published on I'm gonna set to the local daytime now actually the updated one I'm not going to set at all that can be null in the database so what happens on the front end when we take a look at this if if it's if it's not we're not going to display it like it was just created on this date and time or it was published on this day and time if it's not in all then we're going to say hey it might have been created last year but hey I updated it uh at this time and date so so we have that um let's go ahead and create a uh Getters and Setters so let's do that so I'm going to go ahead and create these going to go ahead and create a tostring and we probably could have gotten away without talking about posts but I just want to kind of bring some real world example in here um before we create our controller we're going to create a repository so that we just don't have everything to do with security here I want this to be more like hey here's how we open up our API you know so we'll talk about everything as we go throughout this but I need a post repository that's going to be an interface that extends crud repository so that is of type post the ID type is a long so we'll do that that gives us everything we need there we're going to create a new controller we'll call this post controller inside of post controller I will go ahead and Mark this as a rest controller request mapping and we will say slash API slash posts so I need an insta instance of that repository so we'll say private final post repository we'll call this posts we'll get it through Constructor injection so we have some dependency injection happening there and we'll just have two methods here one to get all of the posts so that actually returns an iterable of posts and we'll call this find all and we can return post.final and finally we have a git mapping so if we take in a ID so it'll be slash ID I'll go ahead and return the post so we're going to call this find by ID and a little trick I talked about this in a recent video if you want to go ahead and look at it I will leave a link in the description and hopefully a card up top here we're talking about we can assign using a domain class converter the ID and what we'll do what it will do is go out to the credit repository and do a find by ID for us so we can just say post post and return posts so we don't have to do that post repository look up by ourselves that should just get automatically converted for us to a post so there's our post we have a repository we need to do some configuration now so I'm going to go in here and create a new class called security config this is going to be a configuration class uh this is also going to enable web security oh we didn't include Spring Security so that's okay um sometimes I forget things I'm getting old let's go ahead and include another dependency so we'll say dependency and we're going to include whoops we're going to include spring boot starter security from there and we'll go ahead and refresh maven everything looks like it's okay so now I can enable web security all right so so far so good The Next Step here is we need to configure Spring Security so what I'm going to do is start by creating a bean not a beam Dan a beam and this is going to be of type security filter chain we'll call this security filter chain this takes in a HTTP security and again if you're kind of new around here that's okay we welcome you I've done a bunch of videos on security and in security configuration in general lately and this is kind of the new approach to configuring Spring Security you used to have to extend a web security configure adapter that's no longer the case we've deprecated that abstract class in favor of a more component based model so we're going to create this bean and that is going to return that HTTP dot build at the end of the day this is going to throw an exception and we're going to go ahead and configure some things here so I am going to authorize um requests and this will take it off auth dot so any requests we want to go ahead and make sure we're authenticated but we will have some exceptions here so what about the case for our MVC matches on slash API slash posts we want to go ahead and permit all so that's one um the other thing we got to do and again I've talked about this in a previous video is we're going to use H2 today and when we want to use H2 we have this H2 Council right that we can view like tables and columns and run SQL commands and things like of that nature we want to make sure that that is enabled for us without any kind of security stuff we don't want to log into the H2 Council again it's only a development thing so not a big deal so what I'm going to do here is cross-site request forgery we're going to say csrf.ignoring ant matchers so again I've talked about this in a previous video but we need to do that we need to say ant managers H2 counsel permit all and finally we need to just say on the header side I want you to go ahead and in the frame options go ahead and allow oops frame options dot same origin so that will give us everything we need to then go ahead and view the H2 Council okay so we're starting to get there we've set up a post we have a post repository to save new posts we have this post controller that we can hit and view all or view one and we've got a security security config we've basically overridden the default security configuration that Booth gives us out of the box and we've said here's what security looks like in our system now we we currently don't have any users and that's okay we don't need a user because we can go ahead and view the H2 console we can go ahead and hit slash API slash posts we just need to do a couple more things so on the application.property side we need to configure a couple properties first off we need to say that hey that H2 console I want to go ahead and enable that because I'd like to go ahead and view that on the data source side we generate a unique name in H2 we don't want to do that so I don't want to generate a unique name because for my data source name I am where is that I'm going to go ahead and set that to something like block finally on the GPA side show SQL I want to go ahead and say true that just does some logging in the console for me and lets me know what's going on so last but not least we need to get some at least one post into our database so we can see it so the way that I'm going to do that is I'm going to create a bean of type command line Runner it says command line Runner is going to allow us to do some things execute some code and this command line Runner is a functional interface that's why we're returning this Lambda expression here and what we want to do is we actually want to use the post repository so let's say post repository posts to go ahead and insert some data again the command line Runner is something that will be created for us and be executed after the application context has been created and before the main application runs that's why we are able to get a instance of our post repository so all I'm going to say here is posts.save new post and what is our post Constructor look like we need a title slug content and author so a new title is going to be hello world our slug is something like this hello world our um what is it content um welcome to my blog and then we need an author uh we'll just say Dan Vega so with that I think we have done everything we need to to get this up and running let's see if we've written a whole bunch of code without any errors I'm going to hopefully say yes but let's see let's go ahead and run this application and see what happens hey we did it we have some things going on here so we do have a user I forgot to mention that because we didn't override the default user yet there is a user we're not going to really worry about that user you see some inserts happening a single insert here for post so let's go over to the browser and take a look at the H2 console all right so here I am again my data source name is blog I'm going to go ahead and connect to that database and you see we haven't created any schema because in the GP world there's some configurations happening underneath the hood to say go ahead and create that table for me and create those columns based on the fields in that entity so if I go ahead and select star from post and run that you can see that we have all those fields in there and then again that updated on is null that's exactly what we want so so far so good and then I guess let's go back to the browser we can always go to slash API slash posts we can see all of the posts we can go to slash API post slash one oh and we did not open that up okay so no no worries uh not a big deal Okay so a lot of this is the same right what we want to do is go back to the security config and talk about users so we have a user in here so we use in previous examples we've created an in memory user we've also we also talked to a database using the jdbc user details manager so the way that we do that is we can go ahead and create beans right so we say Bean user let's say in memory user details manager and then if we look at using in-memory user details manager this implements a user details manager and there are um oops I want to go there and then there are two implementations of user details manager the in-memory user details manager and the jdbc user details manager so those are the two examples that we looked at of of creating users but what we really want is a jpa user details manager and there wasn't there isn't one built into Spring Security so we're going to have to kind of roll our own again I said this at the kind of top of the video that's the bad news the good news is this isn't that hard there is actually a pretty simple easy way to get this done so we're going to save this and what we need to do now is create a new service that implements that you user details service so I'm going to go into the service package I'm going to create a new Java class I'm going to call this jpa user details service and we're going to go ahead and implement the user details oops details service and now it's going to say hey uh you're implementing this interface I need you to go ahead and Implement a method in there there's only one method so we have to implement that method this is load by username takes in a username and if it can't be found it throws a username exception so this what's important to understand here is that this is just a method that we are going to implement right now this has nothing to do with looking up users in a database this is simply a method that we have to implement so what we could end up doing is returning a hard-coded user if we want or a user that we look up in a database using something like spring data jpa so let's start with the fact that we need to return a user details so what is the user details this is the core user information so implementations are not used directly by Spring Security for security purposes this simply stores user information which is later encapsulated into the authentication object so this is the contract that we have to meet at some point so before we get there I don't want to talk about this right now this is what we're gonna have to use eventually now we need to come back and start thinking about the users so what I want to do is create a new Java class and model we're going to call this user and this is just going to be an entity the same way that post was an entity so we're going to create an entity again this is going to have an ID annotation a generated value annotation this is going to be of type 1 we'll call it ID and it will also have things like hey I need a username I need a password and I need um a bunch of rolls so right now we're just going to store this in a simple string you can obviously move this out into another table or an entity right now I'm just going to leave it there and speaking of I do want to store this in a table called users not user so I'm going to change that table name so with the jpa entity we're going to need that noar Constructor we're going to need a Constructor to create a new user so we'll say string username string password string rolls that looks good we'll need some Getters and Setters for all of these so I'll go ahead and create that and then I'll go ahead and create a new tostring looks good so now we have a user that we can store in the database right now that we have a user let's go into a repository package we're going to create a new user repository again that's of type interface it extends credit repository of type user so make sure you use the right user here there's a bunch of different users in here even one by Spring Security make sure we use ours and that type is going to be long so with our user and our user repository we could go ahead and add some users to the database right we can come in here and say um user repository users and we can go ahead and add some users here not a post come on copilot get with it so I can say users dot save and then I'm going to create a new user so again we're using that dev.dan Vega user and I'm going to create one called user it's going to have a password of password for now and it's going to have a roll of roll whoops or underscore user and then let's create one more we'll say that this one is going to be admin password and then this is also going to have a row of admin so again I'm just taking a simple string we can go ahead and split on the comma so it's a comma separated list of roles and that's what I'm going to use as the rules again you can kind of move that out into a separate table if you wanted to this will work fine for my instance so I just want to make sure that everything is working as I expected right now we see some insert into users users and posts so we should be able to go back to the H2 Council go ahead and log in and now we see our users table and if we run that we see our two users user and admin all right so now that I have my users in the database how do I authenticate against those so I could come in here to the jpa user details service and I could try to return that user that we created so we have this user entity but as you can see that's not going to satisfy the contract of this method which is to return a user details so we have this user details interface now one thing you might think to do is come in here and just say Implement user details and that is what I've done in the past I do want to share out a nice little tip here that I picked up from lar over on Twitter who has a really great YouTube channel who has a book on Spring Security I've learned a lot from him you know over the years so definitely go ahead and check him out thanks Laura for for the tip here but instead of polluting my user entity and having all these other user details um things inside of here what I want to do is actually split that out and I want to go ahead and create a new class we're going to call this security user and what we want to do here is Implement user details here so now we'll go ahead and Implement any of the methods that are in here and now we have all of those methods so I don't know why it puts these in such a weird order so I want to do username and password and let's see our authorities and then our account expired so these four I'm actually just gonna set to true true and true okay so with those in place what I'm going to do is actually just defer to my user now so I'm still kind of using the user entity that I've created and the security user but now I have you know separation of concern I've kind of separated those two things one is a user it's an entity that I'm storing in the database the other one is the user that I need to authenticate again so I'm going to get an instance of my user here and we'll do that through a Constructor so now when I go ahead and populate the security user I'm just sending in a user and now I can say user.getusername and I can say user.getpassword and then for the collection here of authorities what I'm going to need to do is I'm going to have to stream this because my user.git roles returns a string and what I want to do with that is actually split so I don't know why I'm getting this weird some splitting on the comma and then once I have those so let's say user Dot here let's just make this a little bit more readable so user.get roles we're going to split on that then what I'm going to do is go ahead and copilot here messing with me so what I want to do is map that to a simple granted Authority right and then what I want to do is return a list so that should give me what I need um nope missed something there so we're splitting so we're getting the rolls which is just a string so it has like roll roll underscore user comma roll underscore admin we're splitting on that comma and then we are mapping that to a simple granted Authority so again this is our granted Authority creating a new instance of that and just returning a list and then the rest of these were just hard coding to true for now to satisfy this so now we have a security user now we have something we can work with because the security user implements user details we can go ahead and return that so now what we need to do is okay we need to check to see if the username that is getting passed to us is a valid one if it's not we're going to throw a username username not found exception so to do this we need an instance of that user repository that we created so I'm going to say private final user repository user repository we'll get that via Constructor injection and now I can say user repository dot find by username and pass in my username now you can see we're getting a little error here we don't have a find by username method in our user repository but that's okay we can go ahead and create one so we can say is we want to get an optional back of type user so that's our user that we created before right and oops when I did that we just want to import optional and then this find by username is a query method it's being derived from the name so the query is being derived from by username so it knows to basically select star from user where username is equal to whatever parameter we're passing in here so that'll give us what we need that'll return a user so what we want to do with that though is we want to um let's go ahead and return this we can do this uh pretty so we can say find my username and if you find somebody I want you to go ahead and map it to that security user that we just created or else go ahead and throw we need to satisfy the contract here of throwing a username not found exception so I'm going to say new username not found exception and I'll just say username not found and we could pass in the username that was given to us so there we go so now we have this one method implemented so we have a load by username so when someone logs in with say a user and password Spring Security is first going to um well we're not going to they're not gonna do it yet there's one more step we have to do but they are going to end up passing this username to this load by username method if we have a username in our database then we will say yes we found this one go ahead and try to authenticate with that user if not it will throw a username not found exception and then we won't have to authenticate against it so with this in place we are almost there back in our security config there's one more thing we need to do we've not told our security configuration where our user details Service is like what you know we didn't set that up yet and if we look at our GPA user details service it's actually not being handled by Spring yet it's not in the application context so I'm going to mark this with add service and now we can get an instance of that right so what we want to do in here is say user details service so hey I need a user Detail Service here how can I pass this instance in so knowing that it's created and we you know spring is managing an instance of that in the application context for us we can say private final user details service there's only one and actually we could be specific here we can say jpa user details service jpa user details service and we'll get that via Constructor injection and now we can say jpa user details service and now spring knows Spring Security knows hey this is the user Detail Service that I need to do when I need to go look up that user by a username so let's just double check to make sure that we've done everything so we have our security filter chain here we're saying hey here's our user details service this is getting injected via Constructor injection we have our user repository to go out and find a user by a username if we find it go ahead and map that user so basically map that to a security user so it will end up passing that user into the Constructor you because it's method reference here and it's going to get us a security user back so that security user will return will satisfy this user details contract so let's see if this works let's go ahead and run this okay no errors that's good hi Laura how you doing let's see if we can still get here we have our users still so user and password so what I want to do is actually go to localhost 8080. this is going to ask us to authenticate I think I still have one from before so let's go ahead and restart this um now do we not set that up yet no we just have a post controller let's do something to test this out real quick let's go into our controller let's create a new Java controller we'll call this home controller we'll have this as a rest controller let's say that we have a on the git mapping so we have a home method public string home so that means really anybody should be authenticated to hit that let's say that we have a user mapping so public string user hello user and let's say we have a get mapping for admin public string admin and we're going to return hello admin okay so those are our three methods so really both of them have the user um role so either users should be able to get to all three of these and then the admin should only be able to get to this one so to fix this we need one more thing in our security configuration we need to enable method level security so that will fix that and then we just need to say hey pre-authorizes should have a whoops has roll um nope has roll roll underscore user and that should be that so that is for there this one you should be an admin for let's go ahead and restart and see if we can attempt to log in so um let's just go to the root all right so something I just realized here is we didn't actually turn on how we're going to log in so you either need to have HTTP basic authentication or a form login so I'm just going to say HTTP basic we'll say with defaults here I'm going to go ahead and restart and everything looks okay let's try and do this again okay so now it's going to ask for a username and password so if we just hit anything that username is not going to be found there is a username called user so that will be found but if we put in any password that's not going to authenticate so we want to authenticate with user and password user and password what did we do for that let's see here so we have user and password okay I know why this is happening so one more thing we need to look at here is when we are authenticating against users we need to set the password encoder so in here let's just do so we're going to say Bean password encoder password encoder and we're going to return the no op password encoder dot get instance now do not do this in production do not do this we're going to come right back and we're going to change this to an actual password encoder like bcrypt just for real quick though I just want to make sure this works okay so let's go ahead and refresh this we'll say admin or actually let's say user and password cool so to let us in it'll let us visit the user endpoint and it will not let us visit the admin password the admin password endpoint so so far everything's working exactly as we hoped now again we are storing plain text passwords in the database we don't want to do that this was just for a demo so let's fix that so what I want to do is I want to go ahead and use a new B Crypt password encoder so that'll work so now what happens here when I set up this user oops right here so now what happens when I set up this user I need to encode that password so I'm going to get an instance of that password encoder we'll call this encoder and then here instead of just storing password in the database I'll use the encoder and say encode and password right so now let's go ahead and take this do the same for admin save that let's go ahead and restart and what I want to look at real quick is the H2 Council so I'll say H2 Council I'm going to go ahead and log in here look at the users and we can see that our passwords are now encoded but if I go to home we'll probably have to restart this again let's just open up a Incognito here so we'll say localhost 8080. so now it's going to ask for admin and password so I'm typing in password and it let me view that it's going to let me view the user endpoint because I also have a user role and it should let me visit the admin endpoint because I have an admin role so cool we were able to encode that password we're no longer storing plain text passwords in the database which of course is a very bad thing to do so I think that's it um as I mentioned before let's just kind of recap a couple things and then I'll mention one more thing so the security config is the big thing in the security config you are defining your user details service like how are we going to um where is this class that has uh implemented the user details service so we are creating that this is called GPA user details service we Implement that it's a single method we got to override and what we are doing there is using our user repository to look for a user with the username that is being passed to us if we find one let's go ahead and map it to that security user that we created to kind of separate those concerns from the user entity itself so very nice tip there also we are throwing a username not found exception if we don't find that user so pretty cool stuff here uh not so hard to do once you kind of see everything that's kind of going on in here uh and again this was the next Evolution the next step in the progression that we've been taking in a lot of these security videos as I mentioned at the beginning I know we're using H2 here this is a very simple switch if you want to use something like my SQL or postgres in fact I've done this in the past with like a Docker compose file to quickly spin up a you know an instance of postgres on my machine without having to install it so if that's something you're interested in I can put a video together on that just let me know in the comments below and if you found value in this video I hope you did I hope you're here learning something new today if you did do me a favor uh go ahead and hit that Thumbs Up Button subscribe to the channel and as always friends happy Cody [Music]
Info
Channel: Dan Vega
Views: 13,304
Rating: undefined out of 5
Keywords: dan vega, spring security jpa, spring boot, spring security, java, spring boot security, spring framework, spring, spring security tutorial, spring security authorization, spring security example, jpa, spring security using spring data jpa and spring boot, spring security authentication, authentication, authorization, spring boot tutorial, spring data jpa, java tutorial, spring boot security example, spring data, spring security authority, spring authorization
Id: awcCiqBO36E
Channel Id: undefined
Length: 40min 4sec (2404 seconds)
Published: Fri Sep 30 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.