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

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();
}
}

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);
}

Spring OAuth - Reload resourceIds and authorities of authentication

I just apply Spring Boot and Spring Cloud to build a microservice system. And I also apply Spring Oauth to it. Honestly, everything is perfect. Spring does a great job in it.
In this system, I have a microservice project does the job of an OAuth server, using JDBC datasource, and I using Permission based for UserDetails authorities (1 User has several Permissions). There are several microservice project does the jobs of Resource server (expose Rest api using Jersey), access security is based on Permissions of Authentication of OAuth bearer token.
Resource Server OAuth config class is something like this
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/restservice/object/list")
.hasAuthority("PERMISSION_VIEW_OBJECT_LIST");
// ...
}
#Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.resourceId("abc-resource-id")
.tokenStore(new JdbcTokenStore(dataSource()));
}
#Bean
#ConfigurationProperties(prefix = "oauth2.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
Everything is great! But I encounter 2 problems:
If I add a new microservice project as a new resourceId, and I append resourceId value to RESOURCE_IDS in table OAUTH_CLIENT_DETAILS of the OAuth client, all requests to Rest API of new resource service return error something like this
{"error":"access_denied","error_description":"Invalid token does not contain resource id (xyz-resource-id)"}
This happens even when user logout and re-login to obtain new access token. It only works if I go to delete records of the Access token and Refresh token int table OAUTH_ACCESS_TOKEN and OAUTH_REFRESH_TOKEN in database.
If at runtime, Permission of a User is changed, the authorities of authentication is not reloaded, I see that AUTHENTICATION value of the Access Token in table OAUTH_ACCESS_TOKEN still contains old Authorities before Permission is changed. In this case, User must logout and re-login to obtain new Access Token with changed authorities.
So, are there any ways to fix these 2 problems.
I'm using Spring Cloud Brixton.SR4 and Spring Boot 1.3.5.RELEASE.
If you are using the default Spring JdbcTokenStore, then the users authentication is serialised and stored with the access/refresh token when the user authenticates and retrieves their token for the first time.
Each time the token is used to authenticate, it is this stored authentication that is loaded which is why changes to the user permissions or the addition of extra resources is not reflected in the users permissions.
In order to add in some checking on this, you can extend DefaultTokenServices and override the loadAuthentication(String accessTokenValue) method to perform your own checks once the users authentication is loaded from the token store.
This may not be the ideal way of doing this, but it is the only way we've found of doing it so far.
To override DefaultTokenServices, add the follwoing bean method to you AuthorizationServerConfigurerAdapter config class:
class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Bean
public AuthorizationServerTokenServices authorizationServerTokenServices() throws Exception {
// Where YourTokenServices extends DefaultTokenServices
YourTokenServices tokenServices = new YourTokenServices();
tokenServices.setTokenStore(tokenStore);
tokenServices.setClientDetailsService(clientDetailsService);
return tokenServices;
}
}
I resolved reload problem this way.
#Bean
public ClientDetailsService jdbcClientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}

Spring OAuth2 Client Credentials with UI

I'm in the process of breaking apart a monolith into microservices. The new microservices are being written with Spring using Spring Security and OAuth2. The monolith uses its own custom security that is not spring security, and for now the users will still be logging into the monolith using this homegrown security. The idea is that the new MS apps will have their own user base, and the monolith app itself will be a "user" of these Services. I've successfully set up an OAuth2 Auth Server to get this working and I'm able to log in with Client Credentials to access the REST APIs.
The problem is that the Microservices also include their own UIs which will need to be accessed both directly by admins (using the new Microservice users and a login page) and through the monolith (hopefully using client credentials so that the monolith users do not have to log in a second time). I have the first of these working, I can access the new UIs, I hit the login page on the OAuth server, and then I'm redirected back to the new UIs and authenticated & authorized.
My expectation from the is that I can log in to the OAuth server with the client credentials behind the scenes and then use the auth token to have the front end users already authenticated on the front end.
My question is - what should I be looking at to implement to get the client credentials login to bypass the login page when coming in through the UI? Using Postman, I've gone to http://myauthapp/oauth/token with the credentials and gotten an access token. Then, I thought I could perhaps just GET the protected UI url (http://mymicroservice/ui) with the header "Authorization: Bearer " and I was still redirected to the login page.
On the UI app:
#Configuration
#EnableOAuth2Client
protected static class ResourceConfiguration {
#Bean
public OAuth2ProtectedResourceDetails secure() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setId("secure/ui");
details.setClientId("acme");
details.setClientSecret("acmesecret");
details.setAccessTokenUri("http://myoauthserver/secure/oauth/token");
details.setUserAuthorizationUri("http://myoauthserver/secure/oauth/authorize");
details.setScope(Arrays.asList("read", "write"));
details.setAuthenticationScheme(AuthenticationScheme.query);
details.setClientAuthenticationScheme(AuthenticationScheme.form);
return details;
}
#Bean
public OAuth2RestTemplate secureRestTemplate(OAuth2ClientContext clientContext) {
OAuth2RestTemplate template = new OAuth2RestTemplate(secure(), clientContext);
AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(
Arrays.<AccessTokenProvider> asList(
new AuthorizationCodeAccessTokenProvider(),
new ResourceOwnerPasswordAccessTokenProvider(),
new ClientCredentialsAccessTokenProvider())
);
template.setAccessTokenProvider(accessTokenProvider);
return template;
}
}
SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private OAuth2ClientContextFilter oAuth2ClientContextFilter;
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.anonymous().disable()
.csrf().disable()
.authorizeRequests()
.antMatchers("/ui").hasRole("USER")
.and()
.httpBasic()
.authenticationEntryPoint(oauth2AuthenticationEntryPoint());
}
private LoginUrlAuthenticationEntryPoint oauth2AuthenticationEntryPoint() {
return new LoginUrlAuthenticationEntryPoint("/login");
}
}

Spring - Best way to control session creation

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);
}
}

Resources