|
| 1 | +--- |
| 2 | +slug: /quick-start/java-spring-boot |
| 3 | +sidebar_label: Java Spring Boot |
| 4 | +sidebar_custom_props: |
| 5 | + description: Spring Boot is a web framework for Java that enables developers to build secure, fast, and scalable server applications with the Java programming language. |
| 6 | + logoFilename: 'spring-boot.svg' |
| 7 | +--- |
| 8 | + |
| 9 | +import FurtherReadings from '../../fragments/_further-readings.md'; |
| 10 | + |
| 11 | +import ScopesAndClaims from './_scopes-and-claims.mdx'; |
| 12 | + |
| 13 | +# Java Spring Boot integration guide |
| 14 | + |
| 15 | +This guide will show you how to integrate Logto into your Java Spring Boot application. |
| 16 | + |
| 17 | +:::info |
| 18 | +You may find the sample code for this guide in our [spring-boot-sample](https://github.com/logto-io/spring-boot-sample) github repository. |
| 19 | +::: |
| 20 | + |
| 21 | +## Prerequisites |
| 22 | + |
| 23 | +- A [Logto Cloud](https://cloud.logto.io) account or a self-hosted Logto (Check out the [⚡ Get started](../../../docs/tutorials/get-started/) guide to create one if you don't have). |
| 24 | +- Our sample code was created using the Spring Boot [securing web starter](https://spring.io/guides/gs/securing-web). Following the instructions to bootstrap a new web application if you don't have one. |
| 25 | +- In this guide, we will use the [Spring Security](https://spring.io/projects/spring-security) ans [Spring Security OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2) libraries to handle OIDC authentication flow with Logto. Please make sure to go through the official documentation to understand the basics. |
| 26 | + |
| 27 | +## Adding dependencies |
| 28 | + |
| 29 | +For [gradle](https://spring.io/guides/gs/gradle) users, add the following dependencies to your `build.gradle` file: |
| 30 | + |
| 31 | +```groovy |
| 32 | +dependencies { |
| 33 | + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' |
| 34 | + implementation 'org.springframework.boot:spring-boot-starter-web' |
| 35 | + implementation 'org.springframework.boot:spring-boot-starter-security' |
| 36 | + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +For [maven](https://spring.io/guides/gs/maven) users, add the following dependencies to your `pom.xml` file: |
| 41 | + |
| 42 | +```xml |
| 43 | +<dependency> |
| 44 | + <groupId>org.springframework.boot</groupId> |
| 45 | + <artifactId>spring-boot-starter-thymeleaf</artifactId> |
| 46 | +</dependency> |
| 47 | +<dependency> |
| 48 | + <groupId>org.springframework.boot</groupId> |
| 49 | + <artifactId>spring-boot-starter-web</artifactId> |
| 50 | +</dependency> |
| 51 | +<dependency> |
| 52 | + <groupId>org.springframework.boot</groupId> |
| 53 | + <artifactId>spring-boot-starter-security</artifactId> |
| 54 | +</dependency> |
| 55 | +<dependency> |
| 56 | + <groupId>org.springframework.boot</groupId> |
| 57 | + <artifactId>spring-boot-starter-oauth2-client</artifactId> |
| 58 | +</dependency> |
| 59 | +``` |
| 60 | + |
| 61 | +## OAuth2 Client Configuration |
| 62 | + |
| 63 | +Register a new `Java Spring Boot` application in Logto console and get the client credential and IdP configurations for your web application. |
| 64 | + |
| 65 | +Add the following configuration to your `application.properties` file: |
| 66 | + |
| 67 | +```properties |
| 68 | + spring.security.oauth2.client.registration.logto.client-name=logto |
| 69 | + spring.security.oauth2.client.registration.logto.client-id={{YOUR_CLIENT_ID}} |
| 70 | + spring.security.oauth2.client.registration.logto.client-secret={{YOUR_CLIENT_ID}} |
| 71 | + spring.security.oauth2.client.registration.logto.redirect-uri={baseUrl}/login/oauth2/code/{registrationId} |
| 72 | + spring.security.oauth2.client.registration.logto.authorization-grant-type=authorization_code |
| 73 | + spring.security.oauth2.client.registration.logto.scope=openid,profile,offline_access |
| 74 | + spring.security.oauth2.client.registration.logto.provider=logto |
| 75 | + |
| 76 | + spring.security.oauth2.client.provider.logto.issuer-uri={{LOGTO_ENDPOINT}}/oidc |
| 77 | + spring.security.oauth2.client.provider.logto.authorization-uri={{LOGTO_ENDPOINT}}/oidc/auth |
| 78 | + spring.security.oauth2.client.provider.logto.jwk-set-uri={{LOGTO_ENDPOINT}}/oidc/jwks |
| 79 | +``` |
| 80 | + |
| 81 | +## Config the redirect_uri in Logto |
| 82 | + |
| 83 | +In order to redirect users back to your application after they sign in, you need to set the redirect URI using the `client.registration.logto.redirect-uri` property in the previous step. |
| 84 | + |
| 85 | +e.g. In our sample code `http://localhost:8080/login/oauth2/code/logto` is the redirect URI. |
| 86 | + |
| 87 | +## Implement the WebSecurityConfig |
| 88 | + |
| 89 | +### Create a new class `WebSecurityConfig` in your project |
| 90 | + |
| 91 | +```java |
| 92 | +package com.example.securingweb; |
| 93 | + |
| 94 | +import org.springframework.context.annotation.Configuration; |
| 95 | +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
| 96 | + |
| 97 | +@Configuration |
| 98 | +@EnableWebSecurity |
| 99 | + |
| 100 | +public class WebSecurityConfig { |
| 101 | + // ... |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +### Create a `idTokenDecoderFactory` bean |
| 106 | + |
| 107 | +This is required because Logto uses ES384 as the default algorithm, we need to overwrite the default `OidcIdTokenDecoderFactory` to use the same algorithm. |
| 108 | + |
| 109 | +```java |
| 110 | +import org.springframework.context.annotation.Bean; |
| 111 | +import org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory; |
| 112 | +import org.springframework.security.oauth2.client.registration.ClientRegistration; |
| 113 | +import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; |
| 114 | +import org.springframework.security.oauth2.jwt.JwtDecoderFactory; |
| 115 | + |
| 116 | +public class WebSecurityConfig { |
| 117 | + // ... |
| 118 | + |
| 119 | + @Bean |
| 120 | + public JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() { |
| 121 | + OidcIdTokenDecoderFactory idTokenDecoderFactory = new OidcIdTokenDecoderFactory(); |
| 122 | + idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> SignatureAlgorithm.ES384); |
| 123 | + return idTokenDecoderFactory; |
| 124 | + } |
| 125 | +} |
| 126 | +``` |
| 127 | + |
| 128 | +### Create a LoginSuccessHandler class to handle the login success event |
| 129 | + |
| 130 | +We will redirect the user to the `/user` page after a successful login. |
| 131 | + |
| 132 | +```java |
| 133 | +package com.example.securingweb; |
| 134 | + |
| 135 | +import java.io.IOException; |
| 136 | + |
| 137 | +import org.springframework.security.core.Authentication; |
| 138 | +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; |
| 139 | + |
| 140 | +import jakarta.servlet.ServletException; |
| 141 | +import jakarta.servlet.http.HttpServletRequest; |
| 142 | +import jakarta.servlet.http.HttpServletResponse; |
| 143 | + |
| 144 | +public class CustomSuccessHandler implements AuthenticationSuccessHandler { |
| 145 | + @Override |
| 146 | + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, |
| 147 | + Authentication authentication) throws IOException, ServletException { |
| 148 | + response.sendRedirect("/user"); |
| 149 | + } |
| 150 | +} |
| 151 | +``` |
| 152 | + |
| 153 | +### Create a LogoutSuccessHandler class to handle the logout success event |
| 154 | + |
| 155 | +Clear the session and redirect the user to the home page. |
| 156 | + |
| 157 | +```java |
| 158 | +package com.example.securingweb; |
| 159 | + |
| 160 | +import java.io.IOException; |
| 161 | + |
| 162 | +import org.springframework.security.core.Authentication; |
| 163 | +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; |
| 164 | + |
| 165 | +import jakarta.servlet.ServletException; |
| 166 | +import jakarta.servlet.http.HttpServletRequest; |
| 167 | +import jakarta.servlet.http.HttpServletResponse; |
| 168 | +import jakarta.servlet.http.HttpSession; |
| 169 | + |
| 170 | +public class CustomLogoutHandler implements LogoutSuccessHandler { |
| 171 | + @Override |
| 172 | + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) |
| 173 | + throws IOException, ServletException { |
| 174 | + HttpSession session = request.getSession(); |
| 175 | + |
| 176 | + if (session != null) { |
| 177 | + session.invalidate(); |
| 178 | + } |
| 179 | + |
| 180 | + response.sendRedirect("/home"); |
| 181 | + } |
| 182 | +} |
| 183 | +``` |
| 184 | + |
| 185 | +### Update the `WebSecurityConfig` class with a `securityFilterChain` |
| 186 | + |
| 187 | +[securityFilterChain](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/DefaultSecurityFilterChain.html) is a chain of filters that are responsible for processing the incoming requests and responses. |
| 188 | + |
| 189 | +We will configure the `securityFilterChain` to allow access to the home page and require authentication for all other requests. Use the `CustomSuccessHandler` and `CustomLogoutHandler` to handle the login and logout events. |
| 190 | + |
| 191 | +```java |
| 192 | +import org.springframework.context.annotation.Bean; |
| 193 | +import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
| 194 | +import org.springframework.security.web.DefaultSecurityFilterChain; |
| 195 | + |
| 196 | +public class WebSecurityConfig { |
| 197 | + // ... |
| 198 | + |
| 199 | + @Bean |
| 200 | + public DefaultSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { |
| 201 | + http |
| 202 | + .authorizeRequests(authorizeRequests -> |
| 203 | + authorizeRequests |
| 204 | + .antMatchers("/", "/home").permitAll() // Allow access to the home page |
| 205 | + .anyRequest().authenticated() // All other requests require authentication |
| 206 | + ) |
| 207 | + .oauth2Login(oauth2Login -> |
| 208 | + oauth2Login |
| 209 | + .successHandler(new CustomSuccessHandler()) |
| 210 | + ) |
| 211 | + .logout(logout -> |
| 212 | + logout |
| 213 | + .logoutSuccessHandler(new CustomLogoutHandler()) |
| 214 | + ); |
| 215 | + return http.build(); |
| 216 | + } |
| 217 | +} |
| 218 | +``` |
| 219 | + |
| 220 | +## Create a home page |
| 221 | + |
| 222 | +(You may skip this step if you already have a home page in your project) |
| 223 | + |
| 224 | +HomeController.java: |
| 225 | + |
| 226 | +```java |
| 227 | +package com.example.securingweb; |
| 228 | + |
| 229 | +import java.security.Principal; |
| 230 | + |
| 231 | +import org.springframework.stereotype.Controller; |
| 232 | +import org.springframework.web.bind.annotation.GetMapping; |
| 233 | + |
| 234 | +@Controller |
| 235 | +public class HomeController { |
| 236 | + @GetMapping({ "/", "/home" }) |
| 237 | + public String home(Principal principal) { |
| 238 | + return principal != null ? "redirect:/user" : "home"; |
| 239 | + } |
| 240 | +} |
| 241 | +``` |
| 242 | + |
| 243 | +This controller will redirect the user to the user page if the user is authenticated, otherwise, it will show the home page. Add a sign-in link to the home page. |
| 244 | + |
| 245 | +home.html: |
| 246 | + |
| 247 | +```html |
| 248 | +<body> |
| 249 | + <h1>Welcome!</h1> |
| 250 | + |
| 251 | + <p><a th:href="@{/oauth2/authorization/logto}">Login with Logto</a></p> |
| 252 | +</body> |
| 253 | +``` |
| 254 | + |
| 255 | +## Create a user page |
| 256 | + |
| 257 | +Create a new controller to handle the user page: |
| 258 | + |
| 259 | +```java |
| 260 | +package com.example.securingweb; |
| 261 | + |
| 262 | +import java.security.Principal; |
| 263 | +import java.util.Map; |
| 264 | + |
| 265 | +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
| 266 | +import org.springframework.security.oauth2.core.user.OAuth2User; |
| 267 | +import org.springframework.stereotype.Controller; |
| 268 | +import org.springframework.ui.Model; |
| 269 | +import org.springframework.web.bind.annotation.GetMapping; |
| 270 | +import org.springframework.web.bind.annotation.RequestMapping; |
| 271 | + |
| 272 | +@Controller |
| 273 | +@RequestMapping("/user") |
| 274 | +public class UserController { |
| 275 | + |
| 276 | + @GetMapping |
| 277 | + public String user(Model model, Principal principal) { |
| 278 | + if (principal instanceof OAuth2AuthenticationToken) { |
| 279 | + OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) principal; |
| 280 | + OAuth2User oauth2User = token.getPrincipal(); |
| 281 | + Map<String, Object> attributes = oauth2User.getAttributes(); |
| 282 | + |
| 283 | + model.addAttribute("username", attributes.get("username")); |
| 284 | + model.addAttribute("email", attributes.get("email")); |
| 285 | + model.addAttribute("sub", attributes.get("sub")); |
| 286 | + } |
| 287 | + |
| 288 | + return "user"; |
| 289 | + } |
| 290 | +} |
| 291 | +``` |
| 292 | + |
| 293 | +Once the user is authenticated, we will retrieve the `OAuth2User` data from the authenticated principal object. Please refer [OAuth2AuthenticationToken](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/oauth2/client/authentication/OAuth2AuthenticationToken.html) and [OAuth2User](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/oauth2/core/user/OAuth2User.html) for more details. |
| 294 | + |
| 295 | +Read the user data and pass it to the `user.html` template. |
| 296 | + |
| 297 | +```html |
| 298 | +<body> |
| 299 | + <h1>User Details</h1> |
| 300 | + <div> |
| 301 | + <p> |
| 302 | + <div><strong>name:</strong> <span th:text="${username}"></span></div> |
| 303 | + <div><strong>email:</strong> <span th:text="${email}"></span></div> |
| 304 | + <div><strong>id:</strong> <span th:text="${sub}"></span></div> |
| 305 | + </p> |
| 306 | + </div> |
| 307 | + |
| 308 | + <form th:action="@{/logout}" method="post"> |
| 309 | + <input type="submit" value="Logout" /> |
| 310 | + </form> |
| 311 | +</body> |
| 312 | +``` |
| 313 | + |
| 314 | +## Run and test the application |
| 315 | + |
| 316 | +Run the application and navigate to `http://localhost:8080`. |
| 317 | + |
| 318 | +- You will see the home page with a sign-in link. |
| 319 | +- Click on the link to sign in with Logto. |
| 320 | +- After successful authentication, you will be redirected to the user page with your user details. |
| 321 | +- Click on the logout button to sign out. You will be redirected back to the home page. |
| 322 | + |
| 323 | +## Scopes |
| 324 | + |
| 325 | +<ScopesAndClaims /> |
| 326 | + |
| 327 | +## Further readings |
| 328 | + |
| 329 | +<FurtherReadings /> |
0 commit comments