How can I do relational database-based HTTP Session Persistence in Spring 4? - spring

I need to be able to store the HTTP Session in a relational database in order to do stateless load balancing of my front-end users across multiple front-end servers. How can I achieve this in Spring 4?
I see how one can do this with Redis, however there does not appear to be documentation on how to do this with a relational database e.g. Postgres.

With Spring Session (it transparently will override HttpSessions from Java EE) you can just take SessionRepository interface and implement it with your custom ex. JdbcSessionRepository. It is kind of easy to do. When you have your implementation, then just add manually (you don't need #EnableRedisHttpSession annotation) created filter to filter chain, like bellow:
#Configuration
#EnableWebMvcSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
//other stuff...
#Autowired
private SessionRepository<ExpiringSession> sessionRepository;
private HttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy(); // or HeaderHttpSessionStrategy
#Bean
public SessionRepository<ExpiringSession> sessionRepository() {
return new JdbcSessionRepository();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
SessionRepositoryFilter<ExpiringSession> sessionRepositoryFilter = new SessionRepositoryFilter<>(sessionRepository);
sessionRepositoryFilter.setHttpSessionStrategy(httpSessionStrategy);
http
.addFilterBefore(sessionRepositoryFilter, ChannelProcessingFilter.class);
}
}
Here you have how SessionRepository interface looks like. It has only 4 methods to implement. For how to create Session object, you can look in MapSessionRepository and MapSession implementation (or RedisOperationsSessionRepository and RedisSession).
public interface SessionRepository<S extends Session> {
S createSession();
void save(S session);
S getSession(String id);
void delete(String id);
}
Example solution https://github.com/Mati20041/spring-session-jpa-repository

Now spring boot supports by 'spring-session-jdbc'. You can save session into db with less code. For more example you can look at https://docs.spring.io/spring-session/docs/current/reference/html5/guides/boot-jdbc.html#httpsession-jdbc-boot-sample

Just slap Spring Session on it, and you're done. Adding a Redis client bean and annotating a configuration class with #EnableRedisHttpSession is all you need.

Related

Spring Boot - the best way to read data from database on startup

I would like to read data to List or Map from database on startup.
Which is the best way to do it? The Spring Boot version is 5.
Is the below solution is good?
#Component
public class ApplicationStartup
implements ApplicationListener<ApplicationReadyEvent> {
/**
* This event is executed as late as conceivably possible to indicate that
* the application is ready to service requests.
*/
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
// here your code ...
return;
}
}
I'd like to storage data on static class, but I have doubt that is the best solution.
I don't quite understand what is your motive for doing so but for doing so you can create a bean using #Component and in that bean create a method with annotation #PostConstruct. you can do whatever you want in this method.
Using the ApplicationRunner interface is the best way to run code once the Spring boot context has loaded.
#Component
public class ApplicationStartup implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) throws Exception {
}
}
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html#boot-features-command-line-runner

Is #Order really needed in Spring Boot 2.0.4 OAuth server?

I have this little OAuth server class and I am using Spring Boot 2.0.4 and the spring-security-oauth2-autoconfigure 2.0.0.RELEASE dependency :
#RestController
#SpringBootApplication
#EnableAuthorizationServer
#Order(200) // really needed ?
public class MyOAuthServerApplication extends WebSecurityConfigurerAdapter {
#RequestMapping({ "/me" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
// #formatter:on
}
}
#Bean
#Override
public UserDetailsService userDetailsService() {
UserDetails mary =
User.withUsername("mary")
.password("{bcrypt}$2a$10$B3NUb0x.MYnSfx7WJItrvO/ymEQwLCKQNehmCuA8keL1uTyHizI0i")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(mary);
}
public static void main(String[] args) {
SpringApplication.run(MyOAuthServerApplication.class, args);
}
}
This seems to work well with and without the #Order(200) annotation.
So is this annotation really needed ?
The Order annotation is used to define the injection precedence.
Read more her: https://www.baeldung.com/spring-order
In your case it's because of the EnableResourceServer annotation. And you must keep the annotation.
From the doc:
The #EnableResourceServer annotation creates a security filter with
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER-1) by default, so by
moving the main application security to
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) we ensure that the
rule for "/me" takes precedence.
Please find the tutorial here: https://spring.io/guides/tutorials/spring-boot-oauth2/
You need it if you have another WebSecurityConfigurerAdapter configuration.
For example if you allow users to login via login form with a different UserDetailsService and so on. Then this should be tried before your oauth authentification and thus needs a lower order, for example #Order(199).
Another example would be different configuration for your API access.
If you don't have any other configuration, then you don't need to set the order.
Setting the order to 200 also seems to be an arbitrary value, that should simply be higher then the others and thus executed last.

SpringBoot HandlerInterceptor not intercepting library endpoint

I have a SpringBoot app where I have implemented a HandlerInterceptor to log general information about API usage. I want it to also log requests to Spring Security's OAuth2 endpoint but it does not intercept the request.
#Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
// register the interceptor that will write API usage info to a file
registry.addInterceptor(new ServiceUsageInterceptor());
}
How can I configure the HandlerInterceptor to intercept all requests?
Thanks
This turned out to be unrelated to the interceptor. The usage was being written to a log file using a custom AccessLogValve in the embedded Tomcat. Updating the pattern seemed to resolve the issue.
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
TomcatEmbeddedServletContainerFactory factory = (TomcatEmbeddedServletContainerFactory) container;
CustomAccessLogValve accessLogValve = new CustomAccessLogValve();
accessLogValve.setEnabled(true);
// set pattern
accessLogValve.setPattern("timestamp=\"%t\" local_host=\"%v\" status=\"%s\" remote_host=\"%h\" client_id=\"%q\" uri=\"%r\" execution_time=\"%D\"");
factory.addContextValves(accessLogValve);
}
}

Spring alternative for Factory

May be its a duplicate, Please feel free to tag... I am a newbie to Spring.
I am implementing a UserService for getting user details from different vendors,
So My class Structure is
Interface UserService ->> UserServiceA, UserServiceB
Which user service to use depends upon a field called provider. my code will look something like
public interface ExternalUserService {
ExternalUserDTO getUserDetail(String username);
}
Implementations -
public class GoogleUserService implements ExternalUserService{
#Override
public ExternalUserDTO getUserDetail(String username) {
return user;
}
}
public class FacebookUserService implements ExternalUserService{
#Override
public ExternalUserDTO getUserDetail(String username) {
return user;
}
}
I want to use it in my code in this fashion, I dont know if this is possible, but giving a try to see if its possible
public class ExternalUserManager(String provider) {
String provider;
#Autowired
ExternalUserService service; //This is supposed to come from some factory, dont know how to get it in spring context.
public void doSomething(String username) {
System.out.println(service.getUserDetail(username));
}
}
Had it been in conventional java programming, I would have created a Factory called UserServiceFactory, which would have made the things straight.
Can someone please help me on how much it is possible with spring, and if its possible, then how can I achieve it? We use Spring boot, so no xml config.
You can use a #Bean annotated method with scope 'prototype' as a factory.
Spring will call this method anytime this bean is injected somewhere.
import org.springframework.beans.factory.config.BeanDefinition;
...
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public ExternalUserService externalUserService(UserServiceFactory factory,UserProviderResolver resolver) {
.. create the user service depending on resolver.getProvider()
}
The UserServiceFactory is used to create the specific service depending on the provider name, as you already described.
Create a class UserProviderResolver whith a method getProvider() that returns the provider name for the current request or user.
You can #Autowire the HttpRequest in the UserProviderResolver to get access to the current request.

Custom default headers for REST API only using Spring Data REST

I have a use case where my application hosts REST API and web application and we need to add custom header to REST APIs only. REST APIs are enabled through Spring Data REST. Typically we could use Servlet Filter to achieve this but we need code the logic of isolating requests to our REST API and add the custom headers. It would be nice if Spring Data REST API allows to add a default header to all the responses it generates. What are your thoughts? Don't say I am lazy :)
For folks looking for actual implementation details..
Interceptor
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("adding CORS headers.....");
response.addHeader("HEADER-NAME", "HEADER-VALUE");
return true;
}
}
Java Configuration
#Configuration
public class RepositoryConfig extends
RepositoryRestMvcConfiguration {
#Override
public RequestMappingHandlerMapping repositoryExporterHandlerMapping() {
RequestMappingHandlerMapping mapping = super
.repositoryExporterHandlerMapping();
mapping.setInterceptors(new Object[] { new CustomInterceptor() });
return mapping;
}
}
As Spring Data REST is built on top of Spring MVC, the easiest way is to configure a custom HandlerInterceptor as described in the reference documentation.
With Spring Data REST the easiest way is to extend RepositoryRestMvcConfiguration and override repositoryExporterHandlerMapping, call the parent method and then invoke ….setInterceptors(…) on it.
Finally I managed to make the setup of custom interceptor working also on spring-data-rest 2.4.1.RELEASE.
#Configuration
public class RestMvcConfig extends RepositoryRestMvcConfiguration {
#Autowired UserInterceptor userInterceptor;
#Autowired ApplicationContext applicationContext;
#Override
public DelegatingHandlerMapping restHandlerMapping() {
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(resourceMappings(), config());
repositoryMapping.setInterceptors(new Object[] { userInterceptor }); // FIXME: not nice way of defining interceptors
repositoryMapping.setJpaHelper(jpaHelper());
repositoryMapping.setApplicationContext(applicationContext);
repositoryMapping.afterPropertiesSet();
BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(config());
basePathMapping.setApplicationContext(applicationContext);
basePathMapping.afterPropertiesSet();
List<HandlerMapping> mappings = new ArrayList<HandlerMapping>();
mappings.add(basePathMapping);
mappings.add(repositoryMapping);
return new DelegatingHandlerMapping(mappings);
}
}
I had to override the restHandlerMapping method, copy-paste it's content and add a line repositoryMapping.setInterceptors for adding custom interceptor, in my case the UserInterceptor.
Is there any better way?

Resources