This showcases the capability to integrate your spring-boot application with WSO2 Identity Server for secure authentication using OpenID Connect standard.
Register an OIDC application as mentioned in the README
Add the following dependencies in the pom file in your spring-boot-project.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Need to add configurations related WSO2 to Identity Server and application in order to make secure authnetication.
Create a application.yml
file inside src/main/resources
folder.
Copy the following configurations.
provider:
host: <is-host-name> #Change the host
spring:
security:
oauth2:
client:
registration:
wso2:
client-name : WSO2 Identity Server
client-id: <application-client-id> #Change client-id
client-secret: <application-client-secret> # Change client-secret
authorization-grant-type: authorization_code
scope: openid
provider:
wso2:
issuer-uri: ${provider.host}/oauth2/token
thymeleaf:
cache: false
Example:
provider:
host: https://localhost:9443 #Change the host
spring:
security:
oauth2:
client:
registration:
wso2:
client-name : WSO2 Identity Server
client-id: LQTLEgDFil5Tyf0wS5KWUShkMDEa #Change client-id
client-secret: uFwLrbBKhp74NWT1zBIjXuXuYUa # Change client-secret
authorization-grant-type: authorization_code
scope: openid
provider:
wso2:
issuer-uri: ${provider.host}/oauth2/token
thymeleaf:
cache: false
-
Spring Boot generates a default login page for us. All the endpoint of the application is secured except this /login page.
-
If you try to access any page of your application, it will redirect to WSO2 Identity Server login page since all the pages are secured.
-
If you go to
“/login”
endpoint, you can get the default login page of the spring-boot-security.
- Create a ConfigSecurity class and Extend the WebSecurityConfigurerAdapter.
@EnableWebSecurity
public class ConfigSecurity extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.anyRequest()
.authenticated()
.and()
.oauth2Login().loginPage("/login");
}
}
- When the application tries to call
/login
endpoint, add redirection to"/oauth2/authorization/wso2"
in your Controller. Then that would skip the login page.
@GetMapping("/login")
public String getLoginPage(Model model) {
return "redirect:/oauth2/authorization/wso2";
}
- Even Though SpringBoot generates a default login page for us, we'll usually want to define our own customized page.
- Add a ConfigSecurity class by extending WebSecurityConfigurerAdapter
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
public class ConfigSecurity extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oidc-login")
.permitAll()
.anyRequest()
.authenticated()
.and()
.oauth2Login().loginPage("/oidc-login");
}
}
- Have a Login Controller Class and render your Login page when the browser is redirected to /oauth-login
@Controller
public class LoginController {
private static String authorizationRequestBaseUri
= "oauth2/authorization";
Map<String, String> oauth2AuthenticationUrls
= new HashMap<>();
@Autowired
private ClientRegistrationRepository clientRegistrationRepository;
@GetMapping("/oidc-login")
public String getLoginPage(Model model) {
Iterable<ClientRegistration> clientRegistrations = null;
ResolvableType type = ResolvableType.forInstance(clientRegistrationRepository)
.as(Iterable.class);
if (type != ResolvableType.NONE &&
ClientRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {
clientRegistrations = (Iterable<ClientRegistration>) clientRegistrationRepository;
}
clientRegistrations.forEach(registration ->
oauth2AuthenticationUrls.put(registration.getClientName(),
authorizationRequestBaseUri + "/" + registration.getRegistrationId()));
model.addAttribute("urls", oauth2AuthenticationUrls);
return "oidc-login";
}
}
Create a template at src/main/resources/oidc-login.html
to display the Login option.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>Login with:</h3>
<p th:each="url : ${urls}">
<a th:text="${url.key}" th:href="${url.value}">Client</a>
</p>
</body>
</html>
By default, spring-boot provides /logout
endpoint to provide logout capability. You can use that default endpoint.
Also you need to have a postlogout uri configured.
- Have a ConfigSecurity class by extending WebSecurityConfigurerAdapter
@EnableWebSecurity
public class ConfigSecurity extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.anyRequest()
.authenticated()
.oauth2Login()
.loginPage("/login")
.and()
.logout()
.logoutSuccessHandler(oidcLogoutSuccessHandler());
}
@Autowired
private ClientRegistrationRepository clientRegistrationRepository;
private LogoutSuccessHandler oidcLogoutSuccessHandler() {
OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler =
new OidcClientInitiatedLogoutSuccessHandler(
this.clientRegistrationRepository);
oidcLogoutSuccessHandler.setPostLogoutRedirectUri(
URI.create("http://localhost:8080/oauth-login")); //Need to give the post-rediret-uri here
return oidcLogoutSuccessHandler;
}
}
- Create a template at
src/main/resources/login.html
to display the Logout option and have a logout button.
Add the /logout
redirection when user clicks the Logout button.
<div style="float:right">
<form method="post" th:action="@{/logout}" class="navbar-form navbar-right">
<button id="logout-button" type="submit" class="btn btn-danger">Logout</button>
</form>
</div>
If you wish to customize the logout endpoint, follow the steps below.
- Have a ConfigSecurity class by extending WebSecurityConfigurerAdapter and configure the logoutUrl
@EnableWebSecurity
public class ConfigSecurity extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.anyRequest()
.authenticated()
.and().oauth2Login().loginPage("/login")
.and().logout().logoutUrl("/applogout")
.logoutSuccessHandler(oidcLogoutSuccessHandler());
}
@Autowired
private ClientRegistrationRepository clientRegistrationRepository;
private LogoutSuccessHandler oidcLogoutSuccessHandler() {
OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler =
new OidcClientInitiatedLogoutSuccessHandler(
this.clientRegistrationRepository);
oidcLogoutSuccessHandler.setPostLogoutRedirectUri(
URI.create("http://localhost:8080/spring-boot-app"));
return oidcLogoutSuccessHandler;
}
}
- Create a template at
src/main/resources/login.html
to display the Logout option and have a logout button.
Add the /applogout
redirection when user clicks the Logout button.
<div style="float:right">
<form method="post" th:action="@{/applogout}" class="navbar-form navbar-right">
<button id="logout-button" type="submit" class="btn btn-danger">Logout</button>
</form>
</div>
- You can get the user information from org.springframework.security.core.Authentication, org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser class.
- Add a method to handle redirection to
/userinfo
endpoint. Add a method in your controller class to get information from id token.
@GetMapping("/userinfo")
public String getUser(Authentication authentication, Model model) {
model.addAttribute("userName", userName);
model.addAttribute("idtoken", user.getClaims());
LOGGER.log(Level.INFO, "UserName : " + userName);
LOGGER.log(Level.INFO, "User Attributes: " + user.getClaims());
return "userinfo";
}
- Create another template
userinfo.html
atsrc/main/resources/templates
to display the idtoken claims.
<div>
<table class="details">
<tr th:each="instance : ${idtoken}">
<td style="text-align:left;width:100px" th:text="${instance.key}">keyvalue</td>
<td style="text-align:left" th:text="${instance.value}">num</td>
</tr>
</table>
</div>