Spring - Best way to control session creation - spring

I am currently retrieving the session timeout from a database since it should be configurable so I just can't declare it in the web.xml.
In my HttpSessionEventPublisher, I basically retrieve the session object from the HttpSessionEvent and I set the session timeout value that I've retrieved from the database using setMaxInactiveInterval.
Upon investigation, whenever I access a POST url in my site, the HttpSessionEventPublisher is triggered and it creates a new Session object. I would like to control this behavior by only creating a Session object if and only if the user is successfully authenticated (logged in, passing through the AuthenticationProvider)
Is this possible?

The HttpSessionEventPublisher does not create sessions itself. It just translates servlet session events to the equivalent ones of spring security. Actually the creation of sessions is not controlled by spring security, but it can initiate one if needed.
If you just want to set the session timeout only upon authentication, then you may extend the authentication handler you use and set the timeout there.
For example the following code extends SavedRequestAwareAuthenticationSuccessHandler and retrieves the timeout from application properties (instead of database as in your case)
#Component
public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
#Value("#{appProperties['session.timeout']}")
private int sessionTimeout;
private final Logger logger = LoggerFactory.getLogger(AuthenticationSuccessHandler.class);
#Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication authentication) throws ServletException, IOException {
logger.debug("onAuthenticationSuccess");
HttpSession session = req.getSession();
session.setMaxInactiveInterval(sessionTimeout);
super.onAuthenticationSuccess(req, res, authentication);
}
}

Related

Spring Security access any user Authentication object

I'm working on the SpringBoot stateful application. For the administration purpose, I need to be able to access any user session and modify the attributes there.
Right now, I know how to successfully access the current (my) user Authentication object:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
OAuth2User principal = (OAuth2User) authentication.getPrincipal();
but how to do the same for any other user?
Is it possible to find the session by username or something similar? I'd really appreciate the example.
There is no built-in mechanism to do something like what you want, but you can write a custom HttpSessionListener that would save references to active sessions, remove them upon expiration and also expose some methods to manipulate session attributes. You would also probably want to associate some user id with the sessions that you can use to perform user lookup, so registering an AuthenticationSuccessHandler to do that would also be needed.
Your logged in users' manager would look something like this:
#Service
public class LoggedInUsersManagerService implements HttpSessionListener {
// assuming you have some session storage here,
// can be something as simple as just a map
public void sessionCreated(HttpSessionEvent se) {
final HttpSession session = se.getSession();
sessionStore.put(session.getAttribute(USER_ID_ATTRIBUTE), session);
}
public void sessionDestroyed(HttpSessionEvent se) {
final HttpSession session = se.getSession();
sessionStore.remove(session.getAttribute(USER_ID_ATTRIBUTE));
}
public void doSomethingWithUserSession(UserIdType id) {
final HttpSession session = sessionStore.get(id);
if(session != null) {
//do what you need with the session attributes
}
}
}
and your SuccessHandler would look like
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
HttpSession session = request.getSession();
session.setAttribute(USER_ID_ATTRIBUTE, getUserIdFromAUthentication(authentication));
//maybe do something else as well
}
}
You can register the success handler in your spring security configuration, for example like this
http
.oauth2login()
.successHandler(myAuthenticationSuccessHandler)
Keep in mind that manipulating session data while the user is still using your service is not really a good idea, so I wouldn't recommend doing something like this unless you are absolutely required to.

Is it good idea to take user id from SecurityContextholder in spring boot RestController..?

I am developing spring boot rest application for an ecommerce app, suppose i have endpoint /shipping-address which will fetch all the saved addresses for the user, is it good idea to take user id from SecurityContextHolder like
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Long userId;
if (principal instanceof UserPrincipal) {
userId = ((UserPrincipal) principal).getId();
}
or should i pass from the client side in the request body..? which is correct..? If i take from SecurityContextHolder is it problem when it comes to Horizontal scaling..??
Please help, i am new to backend development. Thanks.
Taking the userId from the SecurityContext is a good idea because it will prevent from hacking your application.
If you pass the userId from the client somebody could intercept the request and change the userId.
In regards to scaling it depends how you authenticate the user. If it's basic or token based and does not rely on session information. Everything will be fine.
SecurityContext
There is no problem in using a SecurityContext with Spring MVC.
You could use something like :
#RestController
#RequestMapping(path = "/auth")
#Slf4j
public class AuthResource {
#GetMapping(path = "whoami", produces = MediaType.APPLICATION_JSON_VALUE)
#PreAuthorize("isAuthenticated()")
public ResponseEntity<String> whoami(#AuthenticationPrincipal() UserDetails userDetails) {
String login = userDetails.getUsername();
return ResponseEntity.ok(login);
}
}
The annotation #AuthenticationPrincipal from Spring Security will simplify your code.
Session storage
By default, the session will be backed by the HttpSession (cookie JSESSIONID).
So you'll need something like sticky sessions if using a load balancer.
However, sessions can be stored elsewhere, like in relational databases (JDBC) or in Redis : this is what Spring Session can do.
See also Control the Session with Spring Security.
You can also choose to not use sessions with Spring Security :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

Storing shopping cart for guest user using spring session and spring security

I am new to spring session and spring security. I am developing an e-commerce application. My application will not have logged in user.So whenever a user lands on my application I need to create a session to this user and whenever the user adds item to the cart it should store in session.On Checkout i will store this cart items into the database.I could acheive this functionality by servlets using HTTPsession but as per my knowledge using httpsession is not good practice.So i am planning to implement it in spring session and spring security.My question is i dont have authenticated user so is it possible for spring security to create session for anonymous user.I am using HeaderHttpSessionStrategy
For example i am calling "localhost:8080/token" from my app which creates a session id and send to my client.i store the response session id in my localstorage and planning to send this sessionid as X-Auth-token to my headers from next request.If i do so i dont have any authentication configured in my security config as i dont have logged in user.While implementing my cart do i need to get this request header and store it in session so that session is maintained and cart is saved and retreived from session. Will this create me new session for each new user ?.Or else if i dont have logged in user is it ok to implement HttpSession using servlet?. Please let me know with this code whether my session is stored in redis database or not?Please suggest me way or sample to implement my functionality in efficient way.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
private static final String[] PUBLIC_MATCHERS = { "/css/**", "/js/**", "/image/**", "/book/**", "/user/**", "/**" };
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().disable().httpBasic().and().authorizeRequests().antMatchers(PUBLIC_MATCHERS)
.permitAll().anyRequest().anonymous();
}
#Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy();
}
}
HttpSessionConfig.java
#EnableRedisHttpSession
public class HttpSessionConfig {
#Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}

Set timeout for a session at user login and logout user at the end of session timeout

I try to make a mechanism in Spring that when the user login on the website, I assign for his login session a time of validity. For example, I want to set the session to be active for just 10 minutes and after 10 minutes of inactivity I want to logout the user.
My first implementation is like that:
#Service
public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
request.getSession().setMaxInactiveInterval(60*10); // active session for 10 minutes
super.onAuthenticationSuccess(request, response, authentication);
}
}
But doesn't work expected.
So, I tried another way like that:
public class SessionListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent event) {
event.getSession().setMaxInactiveInterval(60*10); // active session for 10 minutes
}
#Override
public void sessionDestroyed(HttpSessionEvent event) {
event.getSession().invalidate();
}
}
Here, I noticed the event entry in sessionDestroyed function, but invalidate function doesn't work as I would expect, so the user can still navigate on the website.
Have everyone any idea about what is my wrong in my configuration or how I can resolve preferably without add config in web.xml. I saw many responses with add session-timeout in web.xml, but I want to make this configuration from Spring configuration classes. And of course, I'm newbie with Spring and Spring Security.

Call a bean AFTER successful spring security login?

If I have spring security working, how can I have it call a bean to initialize all my user data once it has logged in? I can do a Servlet Filter but it calls that on every request. I want to just call some init code to load some user data into the session after the user logs in.
When the user logs in correctly spring security call an instance of AuthenticationSuccessHandler. What you want to do is create your own bean and use that to perform whatever extra actions you want.
Your class would probably look something like this:
public class YourAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//do whatever you want
super.onAuthenticationSuccess(request, response, authentication);
}
}
Also register your class as a spring bean
<beans:bean id="authenticationSuccessHandler"
class="your.package.YourAuthenticationSuccessHandler"/>
and add it to the form login security configuration as the value of authentication-success-handler-ref

Resources