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
/loginendpoint, 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.htmlto 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/sprinb-boot-app"));
return oidcLogoutSuccessHandler;
}
}- Create a template at
src/main/resources/login.htmlto 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
/userinfoendpoint. 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.htmlatsrc/main/resources/templatesto 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>