 Hello Spring fans, my name is Matt Ravel and today I'd like to show you how to test your Spring Security code. They've added a whole slew of new features in Spring Security 5 that make it easy to test OpenID Connect both as a sign-in mechanism and when you set it up as a resource server using OAuth 2.0. Let's giddy up! This screencast is based on a blog post that we published way back in May of 2021 but you might notice we updated it recently here in February and it now uses Spring Boot 263, Spring Security 561 and if you want you can see all the changes there and updates to the example there. I'm going to go to this GitHub repo which contains the completed code for the example and in here there's also a demo.adoc, adoc for ASCII doc. This is basically a file that I created that has the blog post in a condensed format so there's just simple code that I can copy and make it all work without reading through the paragraphs of text but I would encourage you if you would like to know more about Spring Security test please read the blog post because it contains a lot more detailed information. So in this demo I'll show you how to use Spring Security's mocking with SecurityMockServerConfigurers, Spring in its long class names right and SecurityMockMVCRequestPostProcessors as well as authorization tests for the following patterns. Reactive Web Flux Gateway with OIDC authentication, Servlet MVC REST API with JWT authorization so in that scenario we'll be setting up the Spring Boot application as a resource server and then Reactive Web Flux REST API with opaque token authorization because OAuth2.0 doesn't require that you send a JWT it just requires that you send it a random string of characters and guess what a JWT is a random string of characters of course you can decode it and it'll be you know JSON but by default it is just a random string of characters so JWT can be an opaque token but you might want to use OAuth2's opaque token feature where there is an JWT involved so I'm going to put this on the left here and we have a few prerequisites HTTPIE so I already have that installed and then Java 11 I have that installed as well I recommend SDK man if you don't have Java installed or if you want to manage multiple Java versions you can do things like SDK list Java and it'll show all the different versions that you can install there's a whole bunch of OpenJDK builds now there's Coretto from Amazon there's GralVM there's OpenJDK from Java.net and yeah there's a whole bunch so if you just did SDK install Java it would install Java 17 and there's nothing in this tutorial that wouldn't work with 17 I'm just using 11 because that seems to be what most people are using and then you'll need the Octa CLI as well so if you don't have the Octa CLI installed you can get it from cli.octa.com and the brackets at the end of some of the steps will indicate IntelliJ live templates that I will use so you can find all of my live templates on GitHub at mrable idea live templates and you can see it shows you how to import them into your project so the beauty of this tutorial is you shouldn't even need to use live templates because if you read the blog post or use this demo script you can just copy and paste the code and the cool thing about IntelliJ is if you copy and paste a Java class it'll actually create that whole file for you with all the proper Java code in it so I will show you that but I'll also use the live templates so the first thing is to test a wetlux gateway or a spring cloud gateway with mock oidc login so let's start by creating a spring boot project we're going to hit start.spring.io with spring boot version 263 we'll use a baster of api gateway and then the package name will just be an octa package dependencies are cloud eureka for discovery so it can locate you know other services microservices cloud gateway that's spring cloud gateway wetlux octa and lombok so we'll just go ahead and run that and then we can cd into api gateway oh we're already in it look at that all right and if you don't have an octa account you can register using octa register and I already have one so I'm not going to override that one and then if you already have one you can do octa login and it'll you know basically connect you with your existing fork I already have all that installed so I'm going to do octa apps create and then I'll use api gateway I'm going to use web and octa spring boot starter and the default redirect URIs will work just fine those are the defaults that spring security uses for octa and then you can see it wrote everything to this source main resources application top properties so I'll open this up in intelligent and we'll put it on the right there and then the first thing is to rename that properties file to a yaml file I know some people don't like yaml but some people do so I'm just going to go ahead and rename it and not complain and then I have this yaml syntax to name the application gateway as well as enable discovery and configure octa and then configure Eureka as well so I have a shortcut for that I warned you about my live template so sst gateway config is my shortcut there we are and then just copy these values for the octa domain in id and client secret okay now we're good to go and then add a spring security test dependency to the palm dot xml so I'll do that down here type dependency spring security test take the defaults intelligence nice that it auto completes maven coordinates for us and then scope is test and then we'll create a controller package and a user data and a user data controller in it so start by creating that package and this is the feature I wanted to show you how you can just copy a Java class so if I were to just take this class right here and copy it right there look puts it right in there and then because we did add new dependencies to our palm dot XML you do have to click this little icon to load your maven changes since we're using lombok here you do have to configure your IDE to support lombok if you're going to run any sort of you know build commands in intelligent so lombok project you can see how to install it in your IDE if you were to go to not the four minute demo but install you know clips for instance tells you how to do it tell a j and makes it a lot easier to work with otherwise you'll get errors and you'll be like what's going on why is this working and then user data controllers another class we need to create user data controller I have a shortcut for that and so this is just returning the user's data so we can have access tokens and ID tokens in our browser that we can easily and copy and paste to test endpoints with HTTP and so we're just taking in an authenticated principle here and authorized client that's configured for octa and we're creating that new data object setting the full name the ID token the access token just returning that so not a whole lot going on there and then we're going to create a security configuration in a security package and this will enable open ID connect login and JWT authentication and you'll see this is something that I didn't even realize until recently this is a webflux example but I don't know if you knew this but you know how you just find a security filter chain bean you can do the same thing for regular spring mvc and so I don't know if I'll show you any examples of that today but that's something I recently learned that you don't have to extend I think it's web security configure or something like that you can actually do it this same way in spring mvc so we're we're enabling webflux security and this already has a configuration on it so I don't even think we need this one but I'm going to keep it in here just for you know the fact that it'll work sake because you know I tested this beforehand and I didn't try removing that so I'm not going to try removing it now and so now we need to disable the tests or Eureka lookup during tests because it'll work if you have you know Eureka lookup enabled but you'll just get all kinds of logs in your console that kind of say it can't connect to Eureka so I think it's just nice to get rid of those so we'll create source test resources application test dot yml and then Eureka client and it's got some good code completion in here register there we go false and fetch registry false okay and then we're going to activate that profile using active profiles and then we'll create another controller package under source test java so that's the biggest mistake I made when I was doing this tutorial is I created it in the wrong package so I created like source main java and well tests just don't go there so we're going to call this controller and user data controller test and this is going to leverage web test client and mock oidc login so take a look at it here it configures spring boot test and we have some notes here by default that loads the web application context and provides a mock web environment and then auto configure web test client will initialize a web test client for you that can be injected into the test and it also configures the alternative has used a web flux test which also configures a web test client but the test is limited to a single controller whereas with this one we can test multiple controllers the first test right get no auth returns redirect login that's pretty obvious if you hit that endpoint it's going to prompt you to log in and then if you actually mock out with mock oidc login and id token it shows that if you hit that endpoint you will actually get data back so we can run those tests just by clicking this run button here we do have to enable annotation processing and of course you could also do that from the command line using mvn test all right so the next thing we want to do is create a jwt microservice that has lodge listings using spring data rest and on application load a sample data set will be loaded up by an embedded mongo db instance that's initialized by test containers and then jwt access tokens will be decoded verified and validated locally by spring security in the microservice so let's create a new spring boot app with mongo db spring data rest and eureka support so again spring to six uh baster of listings and octa lombok web data mongo db data rest and cloud eureka and then if we cd into listings open that one up in intelligent and add spring security test and test containers and then don't forget to save and reload your maven settings in intelligent now we're going to rename this to yaml again my listings config you'll see your octa domain let's grab our settings from our last project and also an easy way to get your octa domain is to just run octa login i know the uh message isn't that specific because it says octa org already configured but we are working on changing that and but it has your domain right there and so you could copy and paste that as well so just put that right there and then create a model package under java and we're going to have an airbnb listing model object so it's got an id name summary property type room type bed type cancellation policy and an interface in it make sure and select interface airbnb and you'll see this just has a collection of resource relations and then the path is to listing and we're overriding the save method to have an authority of listing admin so this will require that spring security identifies the user of having that authority and then this repository rest resources pretty slick because it makes it so this repository exposes this model and the crud methods straight to the ui not a great pattern i think thoughtworks calls it a nemic domain model but it's awesome for demo so i'm going to use it and then we're going to create a config package here and rest configuration and this is to tweak the spring data rest responses so configuration annotation we're auto-wiring that repository rest configuration we're setting it to return the body when it creates something by default it won't it'll just return i think a 201 that says it's created and then expose ids it won't do that as well because by default if you're doing rest properly you actually have links to other objects instead of you know exposing the ids to the client but again this is a demo so then we're going to create a security configuration and this will require jwt authentication for all the requests again we're enabling web security this time this is an mvc example it's not web flux we're extending web security configure adapter but like i was saying that can actually be a spring filter chain as a beam and then we're authorizing the quest and your request needs to be authenticated and we're crying an OAuth2 resource server and then this is a special thing that we add for the octa spring boot starter where if you hit the endpoint in a browser it'll return a 401 and you'll actually see an error message by default if you configure this with spring security without the octa starter you would just get a blank page and maybe a message from your browser but we uh we like to show something to the user and update this test here to enable the testing profile and then we'll create application test just for tests so source test resources application test dot amel and when i was practicing this one thing that i noticed is if i spelled the file name wrong then you would see a bunch of messages in your test logs but it still works right so let's just it trying to connect to eureka and i just think it's nice to turn this off for tests since you don't need rika involved so spring cloud discovery enabled and we're going to say false and now we're going to create an mvc test to verify the authorization so we'll do this right in the root package air b and b and so it just as it uses test containers to first of all you know spin up a mongo db container and one thing i've noticed is if you have multiple projects using that test container and it's exposing it all in the same port you will have conflicts because that port is already being used so just be aware of that and then first it starts by you know making sure that hitting that endpoint listing needs to be authorized and then with a valid token you know passing in a jwt that's a nice feature of spring security test and expect that it's okay and then if it hits it without the actual authority that'll return forbidden and then if it has that simple granted authority in there it'll work and the id won't be empty so that's all pretty slick how that works and we can run the test now with the mvn w tests so you might have seen a stack trace at the end there if you scroll up a bit prematurely reached end of stream i've noticed that over here and so i put a note in there that you could ignore that because it doesn't really seem to affect anything i think it's just because the mongo db test container shuts down before the application context does so not a big deal as far as i can tell the last thing we're going to do is test a webflux resource server right we just created a spring mvc resource server with mock opaque token so like i said a resource server that you know conforms to olof can have an opaque token the difference is with a jwt it can do local validation with opaque tokens it generally talks to an endpoint to validate that random string of characters we'll start by creating a theaters microservice it's going to use up here we have dev tools data mongo db reactive webflux olof to resource server and cloud eureka so you might notice we're not using the octa spring boot starter but you can still validate jwt's with octa using it so i'll go into the theaters directory here open that up in intelligent the first thing you'll want to do is add the nimbus olof to oidc sdk and it's required for token inspection and of course spring security tests we'll just add those right here maybe reformat because i like spaces more than tabs but you can choose whatever you like in fact i have recently been made aware that tabs are kind of cool because you know some people like bigger spacing some people like smaller some people only like one space so tabs have that flexibility whereas spaces always are the same we'll create an oidc app on octa with octa cli here octa apps create we'll select the spring boot starter just like we did before and the default redirect uris and then we'll rename the application to properties to yaml again and we'll configure it and then we'll create a location model object this just has that geojson point then a theater object it says an id and a location and then a theater repository and that extends reactive mongo repository you'll notice it doesn't have any rest resource configure to expose it via spring data rest we'll create a controller so this does is uses that theater repository and it returns all the theaters or it allows you to create one but if you're going to create one it requires that theater admin as a role and this is a handy pre-authorized annotation you can also use has role but the difference is has role requires that you actually have a role underscore all caps prefix and with has authority you don't need that now we can create a security package and the security configuration in it as well as a JWT opaque token introspector and so this is basically how you configure spring security to talk to that endpoint and get the authorities as well as everything else from there so it uses the client ID of the client's secret and the introspection uri and then it introspects it enhances it and maps all those authorities from the group's claim on the authorization server to spring security authority now we'll create security configuration and you can see we have that security web filter chain requires everything to be authenticated sets up an OAuth2 resource server and also configures an opaque token versus a JWT based resource server and then has that reactive opaque token introspector and returns that we just created and then again and test will turn off talking to Eureka and then we'll create a theater controller test so this starts with configuring MongoDB again it's got that auto configure web test client that's how it gets this bad boy and then it sets it up on port 27017 if you talk to theater expects it to be unauthorized if you use an opaque token it will return okay so that's spring security's mock opaque token that's very handy there and then mock opaque token again if it doesn't pass in a authority that matches what spring expects then doesn't work and then here if you pass in that theater admin you'll see it works just fine so we can run that with mbn test on you notice the talking to Eureka there that's because we didn't go here and set activated or active profiles right to test so like I said it clutters up your logs but it doesn't make the test fail and then one thing I wanted to point out was uh was down here we've got these notes in here that I think make a good point so if you're mocking uh with mock opaque token and you pass correctly through any authentication API and the mock authentication object will be available for the authorization mechanism to verify so that applies to mock mbc but this is likely why an invalid audience expiration or issuer in the token attributes is kind of ignored during this time kind of test so you can see here if we create a jwt with an expired claim an invalid issuer an invalid audience it actually works so that is something that I'm going to show you how to fix by configuring an audience and uh in making it so even the test pass but your code needs to be a little more robust so in the same way if the web test client or mock mbc mocks a different type of authentication than expected the test might pass as long as the controller injects a compatible authentication type so the test will pass depending on which method the test is expecting to be in the security context holder for example the listing service expects a jwt authentication but the following airbnb listing mbc test will pass so with an opaque token so that's just something to be aware of and now i'll show you how to run everything so first we'll configure a eureka server because we need that for everything to to run together we'll close this one down here and we'll do this in the parent directory and then if you go into eureka you'll need to configure it first of all you need to enable it as a eureka server so in eureka application here you can add enable eureka server and then rename the resources to yaml and we're just going to configure it to run on a different port so there's no conflicts we're just going to call it localhost don't register with itself or otherwise you'll get an infinite loop and the service URL there is just the default and then we can configure the api gateway project to have routes to the various listings so let's go to api gateway application here it's the same class but it's just got a token relay gateway filter factory and this is a pretty slick class this allows you here to just apply that filter factory and it'll forward on any access tokens that you receive when you log into the gateway so that's a really nice and easy to configure and then we'll also create a docker setup so we can run all of these with docker compose so we're going to do take docker that does make their ncds into it so we'll go into docker here and open that up and then we'll add a docker compose file and this has got mongo db in it it's got a mongo data path which we'll define in a moment and api gateway so we're going to build that as a docker container and it sets up the service URL the listings with depends on mongo and eureka and eureka will load up on its ports and then the theater is the last service there and then we can get some mongo db dump files from github so these are for the data sets for the theaters and then for the listings and reviews and we'll put this in a mongo directory so here mongo db data and fetch those two and then we'll get the listing and reviews as well and then pwd so we know we're at and then we need to update this mongo data path right to point to where that is and then we'll create a init dbsh file and copy this code into it so it pulls all that data and then we'll build each service image with this command spring boot 2.3 and above has docker support built in so it'll publish it right to a local docker listing so first of all we're going to do api gateway and we can actually skip the test since we already ran those and then we can run multiple of these at once so some of them are using test containers with mongo db and i mentioned that there will be some conflicts so that's why skip tests and it's faster and now we need to do one for eureka as well exit out of those and cd into docker and now we can run docker compose up and you could also pass in dash d and it would run it as a daemon but i like this view because you can see all the different services starting up and we can open it in eureka 8761 and you can see here that not everything is started yet no instance is available so if we refresh we're starting to get it and look they're all up so that looks good and now we can go to local host 8080 user data that'll prompt us to log in comes back and we have that id token and that access token right and we can also go up here and test theater the large value you can see the scroll bar is really small that's all the data that came from the data we loaded for airbnb and then the listings and reviews you can see all those as well so that's all working and then we can test authorization to a you know that listings endpoint to create a new listing so we'll copy the access token value here make sure you don't get any quotes in there and open up a new terminal and set that as a variable and then we'll run this http post command you'll see that post to listing just a name of test and it has that access token well we get a 403 forbidden because we don't have the proper scopes right expects that list admin authority to accept that post request and so doctor spring boot starter will automatically grab the groups from octa if you have a groups claim configured so we need to log in and configure that so if you do octa login you can get the url there log into your octa admin console and you'll go to security api default authorization server and then claims and so we'll add one called groups and you can add it to the access token or id token i'm just gonna access access token here set the value type to groups and match reddit regex is dot star meaning all of them and then any scope and so you can also add it to the id token um if you want spring security when you do oidc login to add it to your id token we won't need it for this one though um but this we're gonna have to now do an incognito window and then our access token is right here so we can do access token to our new one and then try the request again still didn't work ah i know i i didn't create like the group right so i'll go back here and we'll go to directory groups and the group was listing admin lowercase right save that group and then you have to add your user to it so assign people i only have one in here save it and now that should work but again we have to do the incognito window so grab that url close this one do it again and now access token should have that groups claim in it we could verify it at token dot dev or jwt.io if you look now it has those groups in there so we should be able to do access token and now that will work so create the listing return the body of it and that makes it you know easier to see back to our instructions this shows you what you need to do and you know i should have followed my own instructions right because then i want to know that i need to create that role and add the user so now we're going to change the listings project to have a different audience and so you can see how it works if you change the audience so in the listing projects application dot yaml we're going to add this audience the default is api colon slash last default so we're changing it here have a different audience and then we'll rebuild the listings service image and i'll go ahead and stop the docker containers back here while we do that listings there we are now we'll restart all our containers docker compose up grab our access token again look host 8080 user data set that access token and hit that same listings endpoint you'll see this time it says invalid token the odd this odd claim is not equal to the configured audience so that's a much better way to you know lock things down and make sure it all works now your spring security test will still pass but uh you know that's something to consider yeah well let's see do they still pass i don't think they will because i think you need to pass in an audience now so mvn test let's see i don't know it failed because you know we're trying to talk to that bongo that's running in docker you wouldn't think but you know they're both running in docker so i guess so let's try it again there we go so you can see that audience thing is like even though you're you know mocking parts of the jwt out it might not always be what's happening in a real world scenario if you've configured things different so you know i hope you enjoyed the screencast and uh understand more about the security mock server configurers or spring securities web blocks test support and security mock mvc request post processors and spring mvc's test support of course you can find all the code on github if you go to octa spring security test example and please read the blog post if you want more information because it's got a whole lot more in here than what i showed you but also you can copy and paste all the code without needing my shortcuts if you like this screencast please follow me on twitter you can also follow my team on twitter at octa dev and of course subscribe to our youtube channel smash that subscribe button and come back to watch many more useful tutorials like this with spring boot spring data and just a bunch of cool open source tools i hope you have a great day cheers