Overview:
- OAuth2.0 Authorization Framework, Spring boot 2.5.2 and Java 11 (Deprecated)
- JSON Web Tokens , Spring boot 2.4.4 and Kotlin 1.7.10, (Deprecated)
- OpenID Connect
- SAML V2.0
OAuth2.0 Authorization Framework
OAuth 2.0 is the industry-standard protocol for authorization. OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and living room devices.
- Resource Owner
- The user or system that owns the data that is to be shared.
- Resource Server
- A server that protects the user’s resources is the server hosting the resources. for example Facebook or Google is a resource server.
- Client Application
- The client application that requires access to the protected resources.
- Authorization Server
- Is the server authorizing the client app to access the resources of the resource owner.
These roles are illustrated in this diagram:
For implementing code it used the authorization code. The authorization code flow offers a few benefits over the other grant types. When the user authorizes the application, they are redirected back to the application with a temporary code in the URL.
Creating a Spring Boot Application
In this examples we're integration Spring Boot, OAuth2 and Ldap .ldif file.
- Configuration Spring boot, Authorization Server, Resource Server
- Configuration Spring boot, Client Application.
Authorization Server and Resource Server configuration
Technology
- Spring Boot 2.5.2
- OAuth2
- Java 11
- Maven
- IntelliJ IDEA
Project Structure
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.henry</groupId>
<artifactId>auth</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>auth</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>1.5.5</version>
</dependency>
<!-- Spring LDAP -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
</dependency>
<!-- API, java.xml.bind module -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.2</version>
</dependency>
<!-- Runtime, com.sun.xml.bind module -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8081
servlet:
context-path: /auth
spring:
ldap:
embedded:
base-dn: dc=springframework,dc=org
ldif: classpath:test-server.ldif
port: 8389
url: ldap://localhost:8389/
client:
id: henry
secret: secret
redirect: http://localhost:8082/ui/index
Configuration package.
package com.henry.configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Value("${client.id}")
private String clientId;
@Value("${client.secret}")
private String clientSecret;
@Value("${client.redirect}")
private String uriRedirect;
private final BCryptPasswordEncoder passwordEncoder;
public AuthServerConfig(BCryptPasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Override
public void configure(
AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(clientId)
.secret(passwordEncoder.encode(clientSecret))
.authorizedGrantTypes("authorization_code")
.scopes("user_info")
.autoApprove(true).redirectUris(uriRedirect);
}
}
package com.henry.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.logout()
.permitAll();
;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/* auth.inMemoryAuthentication()
.withUser("henry")
.password(passwordEncoder().encode("123"))
.roles("USER");*/
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups")
.contextSource()
.url("ldap://localhost:8389/dc=springframework,dc=org")
.and()
.passwordCompare()
.passwordEncoder(new BCryptPasswordEncoder())
.passwordAttribute("userPassword");
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Controller
package com.henry.controller;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
@EnableResourceServer
@RestController
@RequestMapping("/rest/hello")
public class HelloResource {
@GetMapping("/principal")
public Principal user(Principal principal) {
return principal;
}
@GetMapping
public String hello(Principal principal) {
return "Hello "+principal.getName();
}
}
Client Application configuration
Technology
- Spring Boot 2.5.2
- OAuth2
- Java 11
- Maven
- IntelliJ IDEA
Project Structure
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.henry</groupId>
<artifactId>authClient</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>authClient</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8082
servlet:
context-path: /ui
spring:
security:
basic:
enabled: false
oauth2:
client:
registration:
auth:
client-id: henry
client-secret: secret
client-authentication-method: basic
scope: user_info
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8082/ui/index
provider:
auth:
authorization-uri: http://localhost:8081/auth/oauth/authorize
token-uri: http://localhost:8081/auth/oauth/token
user-name-attribute: preferred_username
thymeleaf:
cache: false
Configuration package.
package com.henry.configuration;
import org.springframework.context.annotation.Bean;
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;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
@EnableWebSecurity
public class UiSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/**", "/login**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.logout()
.logoutUrl("/logout")
.and()
.oauth2Login()
;
}
@Bean
WebClient webClient(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository,
authorizedClientRepository);
oauth2.setDefaultOAuth2AuthorizedClient(true);
return WebClient.builder().apply(oauth2.oauth2Configuration()).build();
}
}
Controller
package com.henry.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class IndexController {
@GetMapping("/")
public String home1() {
System.out.println("redirect OAuth2 Login");
return "redirect:/oauth2/authorization/auth";
}
@GetMapping("/index")
public String getLoginPage() {
return "index";
}
}
Run & Test
Internally to make this:
https://YOUR_DOMAIN/authorize?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://YOUR_APP/callback&
scope=SCOPE&
state=STATE
Redirect to:
Ldap user :
Username: henry
Password: 123
And back to client, where we get the authorization code for generated Access Token for access API.
Get the code from URL and then we can generated the Access Token, this way:
curl --request POST \
--url 'https://YOUR_DOMAIN/oauth/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=authorization_code \
--data 'client_id=YOUR_CLIENT_ID' \
--data client_secret=YOUR_CLIENT_SECRET \
--data code=YOUR_AUTHORIZATION_CODE \
--data 'redirect_uri=https://YOUR_APP/callback'
POST: http://localhost:8081/auth/oauth/token
If you want to test change the code.
With Access Token we can access for API.
GET: http://localhost:8081/auth/rest/hello?access_token=your_token
JSON Web Tokens
JSON Web Token JWTs can be encrypted to also provide secrecy between parties, we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.
JSON Web Token structure
JSON Web Tokens consist of three parts separated by dots (.), which are:
Diagram:
For implementing code it used the OAuth2 with JWT.
Creating a Spring Boot Application
In this example we're integration Spring Boot, Kotlin.
- Configuration Spring boot, Kotlin, Authorization Server, Resource Server and Spring Security.
- Spring Controllers
Authorization Server and Resource Server configuration
Technology
- Spring Boot 2.4.4
- Kotlin 1.7.10
- OAuth2
- JWT
- Java 11
- Maven
- IntelliJ IDEA
Project Structure
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hxiloj</groupId>
<artifactId>SpringBoot2-oauth2-Ldap</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBoot2-oauth2-Ldap</name>
<description>SpringBoot2-oauth2-Ldap</description>
<properties>
<java.version>11</java.version>
<kotlin.version>1.7.10</kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
</dependency>
<!-- oauth2 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
<!-- jwt -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>1.5.5</version>
</dependency>
<dependency>
<groupId>net.sourceforge.collections</groupId>
<artifactId>collections-generic</artifactId>
<version>4.01</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- API, java.xml.bind module -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<!-- Runtime, com.sun.xml.bind module -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<finalName>auth</finalName>
</build>
</project>
application.yml
server:
port: 9000
servlet:
context-path: /auth
spring:
ldap:
embedded:
base-dn: dc=springframework,dc=org
ldif: classpath:test-server.ldif
port: 8389
url: ldap://localhost:8389/
client:
id: henry
secret: secret
Configuration package.
package com.hxiloj.configuration
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore
@Configuration
@EnableAuthorizationServer
open class AuthorizationServerConfig : AuthorizationServerConfigurerAdapter() {
@Value("\${client.id}")
private val clientId: String? = null
@Value("\${client.secret}")
private val clientSecret: String? = null
@Autowired
@Qualifier("authenticationManagerBean")
private val authenticationManager: AuthenticationManager? = null
@Autowired
private val passwordEncoder: BCryptPasswordEncoder? = null
@Throws(Exception::class)
override fun configure(security: AuthorizationServerSecurityConfigurer) {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
}
@Throws(Exception::class)
override fun configure(clients: ClientDetailsServiceConfigurer) {
clients.inMemory().withClient(clientId)
.secret(passwordEncoder!!.encode(clientSecret))
.scopes("read", "write")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(3600)
.accessTokenValiditySeconds(3600)
}
@Throws(Exception::class)
override fun configure(endpoints: AuthorizationServerEndpointsConfigurer) {
endpoints.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
}
@Bean
open fun tokenStore(): JwtTokenStore {
return JwtTokenStore(accessTokenConverter())
}
@Bean
open fun accessTokenConverter(): JwtAccessTokenConverter {
val converter = JwtAccessTokenConverter()
converter.setSigningKey("123")
return converter
}
}
package com.hxiloj.configuration
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.CorsConfigurationSource
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
import org.springframework.web.filter.CorsFilter
import java.util.*
@Configuration
@EnableResourceServer
open class ResourceServerConfig : ResourceServerConfigurerAdapter() {
@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.authorizeRequests().antMatchers("/webjars/**").permitAll().anyRequest().authenticated().and().cors()
.configurationSource(corsConfigurationSource())
}
@Bean
open fun corsConfigurationSource(): CorsConfigurationSource {
val config = CorsConfiguration()
config.allowedOrigins = listOf("*")
config.allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS")
config.allowCredentials = true
config.allowedHeaders = listOf("Content-Type", "Authorization")
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", config)
return source
}
@Bean
open fun corsFilter(): FilterRegistrationBean<CorsFilter> {
val bean = FilterRegistrationBean(
CorsFilter(corsConfigurationSource())
)
bean.order = Ordered.HIGHEST_PRECEDENCE
return bean
}
}
package com.hxiloj.configuration
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.oauth2.provider.ClientDetailsService
import org.springframework.security.oauth2.provider.approval.ApprovalStore
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory
import org.springframework.security.oauth2.provider.token.TokenStore
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
open class SpringSecurityConfig : WebSecurityConfigurerAdapter() {
@Autowired
private val clientDetailsService: ClientDetailsService? = null
@Bean
@Throws(Exception::class)
override fun authenticationManagerBean(): AuthenticationManager {
return super.authenticationManagerBean()
}
@Throws(Exception::class)
override fun configure(auth: AuthenticationManagerBuilder) {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups")
.contextSource()
.url("ldap://localhost:8389/dc=springframework,dc=org")
.and()
.passwordCompare()
.passwordEncoder(BCryptPasswordEncoder())
.passwordAttribute("userPassword")
}
@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http
.csrf().disable()
.cors().disable()
.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.anyRequest().authenticated()
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
@Bean
@Autowired
open fun userApprovalHandler(tokenStore: TokenStore?): TokenStoreUserApprovalHandler {
val handler = TokenStoreUserApprovalHandler()
handler.setTokenStore(tokenStore)
handler.setRequestFactory(DefaultOAuth2RequestFactory(clientDetailsService))
handler.setClientDetailsService(clientDetailsService)
return handler
}
@Bean
@Autowired
@Throws(Exception::class)
open fun approvalStore(tokenStore: TokenStore?): ApprovalStore {
val store = TokenApprovalStore()
store.setTokenStore(tokenStore)
return store
}
@Bean
open fun passwordEncoder(): BCryptPasswordEncoder {
return BCryptPasswordEncoder()
}
}
Controller
package com.hxiloj.controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.security.Principal
@RestController
class DefaultRestController {
@GetMapping("/principal")
fun user(principal: Principal): Principal {
return principal
}
@GetMapping
fun hello(principal: Principal): String? {
return "Hello " + principal.name
}
}
Run & Test
Ldap user :
Username: henry
Password: 123
POSTMAN
Get access token
POST: http://localhost:9000/oauth/token
Type: Basic Auth
Username: henry
Password: secret
GET: headers with
Key: Authorization
value: Bearer {token}
http://localhost:9000/rest/hello
OpenID Connect
OpenID Connect is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.
OpenID Connect allows clients of all types, including Web-based, mobile, and JavaScript clients, to request and receive information about authenticated sessions and end-users. The specification suite is extensible, allowing participants to use optional features such as encryption of identity data, discovery of OpenID Providers, and logout, when it makes sense for them. in OpenID Connect, OAuth 2.0 capabilities are integrated with the protocol itself.
For implementing code it used OpenId Connect, JWT and OAuth2. however for implementing it is a little a bit extensive for that I posted two examples:
SAML V2.0
SAML version 2.0 was approved as an OASIS Standard in March 2005.
Approved Errata for SAML V2.0 was last produced by the SSTC on 1 May 2012. In addition to the normative errata document, the following non-normative "errata composite" documents have been provided that combine the prescribed corrections with the original specification text, illustrating the changes with margin change bars, struck-through original text, and highlighted new text.
What is SAML?
Security Assertion Markup Language. It is an XML-based open-standard for transferring identity data between two parties: an identity provider (IdP) and a service provider (SP).
For implementing code, I created one post, so I think SAML 2.0 is lit a bit more complex than OpenId Connect, JWT and OAuth2.
Source Code
OAuth2.0 Authorization Framework
JSON Web Tokens
OpenID Connect
GitHub Repositories:
SAML 2.0
References:
https://oauth.net/2/
https://auth0.com/intro-to-iam/what-is-oauth-2/
https://auth0.com/docs/get-started/authentication-and-authorization-flow/add-login-auth-code-flow
https://jwt.io/introduction
https://developers.onelogin.com/openid-connect
https://jenkov.com/tutorials/oauth2/roles.html