Skip to content

Commit caa6fe9

Browse files
authored
Merge pull request #583 from bpstelios10/BAEL-9245-spring-oauth-with-redis
Bael 9245 spring oauth with redis
2 parents a0c0521 + 4a506b2 commit caa6fe9

File tree

41 files changed

+2060
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2060
-2
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<description>Spring Security OAuth Authorization Server Implemented With Redis</description>
5+
6+
<groupId>com.baeldung</groupId>
7+
<artifactId>oauth-authorization-server-with-redis</artifactId>
8+
<version>0.1.0-SNAPSHOT</version>
9+
<packaging>pom</packaging>
10+
11+
<parent>
12+
<groupId>org.springframework.boot</groupId>
13+
<artifactId>spring-boot-starter-parent</artifactId>
14+
<version>3.5.0</version>
15+
</parent>
16+
17+
<modules>
18+
<module>redis-authorization-server</module>
19+
<module>redis-client-server</module>
20+
<module>redis-resource-server</module>
21+
</modules>
22+
23+
<properties>
24+
<java.version>17</java.version>
25+
</properties>
26+
27+
<build>
28+
<plugins>
29+
<plugin>
30+
<groupId>org.apache.maven.plugins</groupId>
31+
<artifactId>maven-dependency-plugin</artifactId>
32+
</plugin>
33+
</plugins>
34+
</build>
35+
</project>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<artifactId>redis-authorization-server</artifactId>
6+
<name>redis-authorization-server</name>
7+
<packaging>jar</packaging>
8+
9+
<parent>
10+
<groupId>com.baeldung</groupId>
11+
<artifactId>oauth-authorization-server-with-redis</artifactId>
12+
<version>0.1.0-SNAPSHOT</version>
13+
</parent>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>org.springframework.boot</groupId>
18+
<artifactId>spring-boot-starter-web</artifactId>
19+
</dependency>
20+
<dependency>
21+
<groupId>org.springframework.boot</groupId>
22+
<artifactId>spring-boot-starter-security</artifactId>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.springframework.boot</groupId>
26+
<artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.springframework.boot</groupId>
30+
<artifactId>spring-boot-starter-data-redis</artifactId>
31+
</dependency>
32+
33+
<dependency>
34+
<groupId>com.github.codemonstur</groupId>
35+
<artifactId>embedded-redis</artifactId>
36+
<version>1.4.2</version>
37+
</dependency>
38+
39+
<dependency>
40+
<groupId>org.springframework.boot</groupId>
41+
<artifactId>spring-boot-starter-test</artifactId>
42+
<scope>test</scope>
43+
</dependency>
44+
<dependency>
45+
<groupId>io.rest-assured</groupId>
46+
<artifactId>rest-assured</artifactId>
47+
<scope>test</scope>
48+
</dependency>
49+
</dependencies>
50+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.baeldung;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class OAuth2AuthorizationServerApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(OAuth2AuthorizationServerApplication.class, args);
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.baeldung.config;
2+
3+
import java.io.IOException;
4+
import java.util.Arrays;
5+
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
import org.springframework.core.annotation.Order;
10+
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
11+
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
12+
import org.springframework.data.redis.core.RedisTemplate;
13+
import org.springframework.data.redis.core.convert.RedisCustomConversions;
14+
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
15+
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
16+
17+
import com.baeldung.convert.BytesToClaimsHolderConverter;
18+
import com.baeldung.convert.BytesToOAuth2AuthorizationRequestConverter;
19+
import com.baeldung.convert.BytesToUsernamePasswordAuthenticationTokenConverter;
20+
import com.baeldung.convert.ClaimsHolderToBytesConverter;
21+
import com.baeldung.convert.OAuth2AuthorizationRequestToBytesConverter;
22+
import com.baeldung.convert.UsernamePasswordAuthenticationTokenToBytesConverter;
23+
import com.baeldung.repository.OAuth2AuthorizationGrantAuthorizationRepository;
24+
import com.baeldung.repository.OAuth2RegisteredClientRepository;
25+
import com.baeldung.repository.OAuth2UserConsentRepository;
26+
import com.baeldung.service.RedisOAuth2AuthorizationConsentService;
27+
import com.baeldung.service.RedisOAuth2AuthorizationService;
28+
import com.baeldung.service.RedisRegisteredClientRepository;
29+
30+
import jakarta.annotation.PostConstruct;
31+
import jakarta.annotation.PreDestroy;
32+
import redis.embedded.RedisServer;
33+
34+
@Configuration(proxyBeanMethods = false)
35+
@EnableRedisRepositories
36+
public class RedisConfig {
37+
38+
private final String redisHost;
39+
private final int redisPort;
40+
private final RedisServer redisServer;
41+
42+
public RedisConfig(@Value("${spring.data.redis.host}") String redisHost, @Value("${spring.data.redis.port}") int redisPort) throws IOException {
43+
this.redisHost = redisHost;
44+
this.redisPort = redisPort;
45+
this.redisServer = new RedisServer(redisPort);
46+
}
47+
48+
@PostConstruct
49+
public void postConstruct() throws IOException {
50+
redisServer.start();
51+
}
52+
53+
@PreDestroy
54+
public void preDestroy() throws IOException {
55+
redisServer.stop();
56+
}
57+
58+
@Bean
59+
@Order(1)
60+
public JedisConnectionFactory redisConnectionFactory() {
61+
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(redisHost, redisPort);
62+
63+
return new JedisConnectionFactory(redisStandaloneConfiguration);
64+
}
65+
66+
@Bean
67+
@Order(2)
68+
public RedisTemplate<?, ?> redisTemplate(JedisConnectionFactory connectionFactory) {
69+
RedisTemplate<byte[], byte[]> template = new RedisTemplate<>();
70+
template.setConnectionFactory(connectionFactory);
71+
return template;
72+
}
73+
74+
@Bean
75+
@Order(3)
76+
public RedisCustomConversions redisCustomConversions() {
77+
return new RedisCustomConversions(
78+
Arrays.asList(new UsernamePasswordAuthenticationTokenToBytesConverter(), new BytesToUsernamePasswordAuthenticationTokenConverter(),
79+
new OAuth2AuthorizationRequestToBytesConverter(), new BytesToOAuth2AuthorizationRequestConverter(), new ClaimsHolderToBytesConverter(),
80+
new BytesToClaimsHolderConverter()));
81+
}
82+
83+
@Bean
84+
@Order(4)
85+
public RedisRegisteredClientRepository registeredClientRepository(OAuth2RegisteredClientRepository registeredClientRepository) {
86+
RedisRegisteredClientRepository redisRegisteredClientRepository = new RedisRegisteredClientRepository(registeredClientRepository);
87+
redisRegisteredClientRepository.save(RegisteredClients.messagingClient());
88+
89+
return redisRegisteredClientRepository;
90+
}
91+
92+
@Bean
93+
@Order(5)
94+
public RedisOAuth2AuthorizationService authorizationService(RegisteredClientRepository registeredClientRepository,
95+
OAuth2AuthorizationGrantAuthorizationRepository authorizationGrantAuthorizationRepository) {
96+
return new RedisOAuth2AuthorizationService(registeredClientRepository, authorizationGrantAuthorizationRepository);
97+
}
98+
99+
@Bean
100+
@Order(6)
101+
public RedisOAuth2AuthorizationConsentService authorizationConsentService(OAuth2UserConsentRepository userConsentRepository) {
102+
return new RedisOAuth2AuthorizationConsentService(userConsentRepository);
103+
}
104+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.baeldung.config;
2+
3+
import java.util.UUID;
4+
5+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
6+
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
7+
import org.springframework.security.oauth2.core.oidc.OidcScopes;
8+
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
9+
10+
public class RegisteredClients {
11+
12+
public static RegisteredClient messagingClient() {
13+
return RegisteredClient.withId(UUID.randomUUID()
14+
.toString())
15+
.clientId("articles-client")
16+
.clientSecret("{noop}secret")
17+
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
18+
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
19+
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
20+
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
21+
.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)
22+
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/articles-client-oidc")
23+
.redirectUri("http://127.0.0.1:8080/authorized")
24+
.postLogoutRedirectUri("http://127.0.0.1:9000/login")
25+
.scope(OidcScopes.OPENID)
26+
.scope("articles.read")
27+
.build();
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.baeldung.config;
2+
3+
import static org.springframework.security.config.Customizer.withDefaults;
4+
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.core.annotation.Order;
8+
import org.springframework.security.config.Customizer;
9+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
11+
import org.springframework.security.core.userdetails.User;
12+
import org.springframework.security.core.userdetails.UserDetails;
13+
import org.springframework.security.core.userdetails.UserDetailsService;
14+
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
15+
import org.springframework.security.crypto.password.PasswordEncoder;
16+
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
17+
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
18+
import org.springframework.security.web.SecurityFilterChain;
19+
20+
@Configuration
21+
@EnableWebSecurity
22+
public class SecurityConfig {
23+
24+
@Bean
25+
@Order(1)
26+
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
27+
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = OAuth2AuthorizationServerConfigurer.authorizationServer()
28+
.oidc(Customizer.withDefaults());
29+
30+
http.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
31+
.with(authorizationServerConfigurer, Customizer.withDefaults())
32+
.authorizeHttpRequests((authorize) -> authorize.anyRequest()
33+
.authenticated());
34+
35+
return http.formLogin(withDefaults())
36+
.build();
37+
}
38+
39+
@Bean
40+
@Order(2)
41+
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
42+
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest()
43+
.authenticated())
44+
.formLogin(withDefaults());
45+
46+
return http.build();
47+
}
48+
49+
@Bean
50+
UserDetailsService users() {
51+
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
52+
UserDetails user = User.builder()
53+
.username("admin")
54+
.password("password")
55+
.passwordEncoder(encoder::encode)
56+
.roles("USER")
57+
.build();
58+
59+
return new InMemoryUserDetailsManager(user);
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.baeldung.convert;
2+
3+
import org.springframework.core.convert.converter.Converter;
4+
import org.springframework.data.convert.ReadingConverter;
5+
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
6+
import org.springframework.security.jackson2.SecurityJackson2Modules;
7+
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
8+
9+
import com.baeldung.entity.OAuth2AuthorizationGrantAuthorization;
10+
import com.fasterxml.jackson.databind.ObjectMapper;
11+
12+
@ReadingConverter
13+
public class BytesToClaimsHolderConverter implements Converter<byte[], OAuth2AuthorizationGrantAuthorization.ClaimsHolder> {
14+
15+
private final Jackson2JsonRedisSerializer<OAuth2AuthorizationGrantAuthorization.ClaimsHolder> serializer;
16+
17+
public BytesToClaimsHolderConverter() {
18+
ObjectMapper objectMapper = new ObjectMapper();
19+
objectMapper.registerModules(SecurityJackson2Modules.getModules(BytesToClaimsHolderConverter.class.getClassLoader()));
20+
objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
21+
objectMapper.addMixIn(OAuth2AuthorizationGrantAuthorization.ClaimsHolder.class, ClaimsHolderMixin.class);
22+
this.serializer = new Jackson2JsonRedisSerializer<>(objectMapper, OAuth2AuthorizationGrantAuthorization.ClaimsHolder.class);
23+
}
24+
25+
@Override
26+
public OAuth2AuthorizationGrantAuthorization.ClaimsHolder convert(byte[] value) {
27+
return this.serializer.deserialize(value);
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.baeldung.convert;
2+
3+
import org.springframework.core.convert.converter.Converter;
4+
import org.springframework.data.convert.ReadingConverter;
5+
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
6+
import org.springframework.security.jackson2.SecurityJackson2Modules;
7+
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
8+
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
9+
10+
import com.fasterxml.jackson.databind.ObjectMapper;
11+
12+
@ReadingConverter
13+
public class BytesToOAuth2AuthorizationRequestConverter implements Converter<byte[], OAuth2AuthorizationRequest> {
14+
15+
private final Jackson2JsonRedisSerializer<OAuth2AuthorizationRequest> serializer;
16+
17+
public BytesToOAuth2AuthorizationRequestConverter() {
18+
ObjectMapper objectMapper = new ObjectMapper();
19+
objectMapper.registerModules(SecurityJackson2Modules.getModules(BytesToOAuth2AuthorizationRequestConverter.class.getClassLoader()));
20+
objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
21+
this.serializer = new Jackson2JsonRedisSerializer<>(objectMapper, OAuth2AuthorizationRequest.class);
22+
}
23+
24+
@Override
25+
public OAuth2AuthorizationRequest convert(byte[] value) {
26+
return this.serializer.deserialize(value);
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.baeldung.convert;
2+
3+
import org.springframework.core.convert.converter.Converter;
4+
import org.springframework.data.convert.ReadingConverter;
5+
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
6+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
7+
import org.springframework.security.jackson2.SecurityJackson2Modules;
8+
9+
import com.fasterxml.jackson.databind.ObjectMapper;
10+
11+
@ReadingConverter
12+
public class BytesToUsernamePasswordAuthenticationTokenConverter implements Converter<byte[], UsernamePasswordAuthenticationToken> {
13+
14+
private final Jackson2JsonRedisSerializer<UsernamePasswordAuthenticationToken> serializer;
15+
16+
public BytesToUsernamePasswordAuthenticationTokenConverter() {
17+
ObjectMapper objectMapper = new ObjectMapper();
18+
objectMapper.registerModules(SecurityJackson2Modules.getModules(BytesToUsernamePasswordAuthenticationTokenConverter.class.getClassLoader()));
19+
this.serializer = new Jackson2JsonRedisSerializer<>(objectMapper, UsernamePasswordAuthenticationToken.class);
20+
}
21+
22+
@Override
23+
public UsernamePasswordAuthenticationToken convert(byte[] value) {
24+
return this.serializer.deserialize(value);
25+
}
26+
}

0 commit comments

Comments
 (0)