Skip to content

Commit fb53d10

Browse files
authored
feat(docs): add spring-boot integration guide (#662)
add spring-boot integration guide
1 parent 889d123 commit fb53d10

File tree

3 files changed

+345
-0
lines changed

3 files changed

+345
-0
lines changed
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
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 />
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import ScopeClaimList from '../../fragments/_scope-claim-list.md';
2+
3+
Logto uses OIDC [scopes and claims conventions](https://openid.net/specs/openid-connect-core-1_0.html#Claims) to define the scopes and claims for retrieving user information from the ID token and OIDC <a href="https://openid.net/specs/openid-connect-core-1_0.html#UserInfo">userinfo endpoint</a>. Both of the "scope" and the "claim" are terms from the OAuth 2.0 and OpenID Connect (OIDC) specifications.
4+
5+
In short, when you request a scope, you will get the corresponding claims in the user information. For example, if you request the `email` scope, you will get the `email` and `email_verified` data of the user.
6+
7+
<ScopeClaimList />
8+
9+
Add extra scopes and claims at the `application.properties` file to request for more user information. For example, to request the `urn:logto:scope:organizations` scope, add the following line to the `application.properties` file:
10+
11+
```properties
12+
spring.security.oauth2.client.registration.logto.scope=openid,profile,offline_access,urn:logto:scope:organizations
13+
```
14+
15+
User organization claims will be included in the authorization token.

static/img/logo/spring-boot.svg

Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)