Not understanding Spring SessionAttribute and Autowiring - spring

I'm not a Spring expert and I'm facing a behavior I don't understand...
I have a SessionAttribute "user" in my Controller, that is autowired to my bean User.
When I log in, my User is populated with some values etc.
When I log out, I am expecting that my session attribute "user" would be reset, but it keeps its values.
So where is the problem? Is my log out not working properly? Or is it normal and so, could someone explain me what is happening inside Spring please?
Here is a code Sample to understand my question:
#Controller
#SessionAttributes("user")
public class HomeController
{
#Autowired
private User user;
// Session Attribute
#ModelAttribute("user")
public User setSessionAttribute()
{
LOGGER.debug("Adding user to session...");
return user;
}
...
}
Edit: logout sample code and user declaration
My User is declared like this:
#Component
public class User
{
...
}
To log out I have a link pointing to /myapp/j_spring_security_logout and I have implemented a logout handler:
#Component
public class MyLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler
{
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException
{
//to check if user is in session, but it's not
Enumeration<String> e = request.getSession().getAttributeNames();
//some code needed to log out from my custom security manager
//kill the session (not spring session) and redirect to the specified url
agent.logout("/myapp/login");
super.onLogoutSuccess(request, response, authentication);
}
}

Now that you've posted User
#Component
public class User
{
...
}
you will notice that it is has Singleton scope. The bean autowired here
#Autowired
private User user;
is that singleton instance. It will always be the same regardless of what Session or request you're processing and regardless of you logging out. So up to now, all your users have been sharing the same User instance.
You can change it to have Session scope.
#Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
#Component
public class User
{
...
}
Now each Session will have its own instance to work with.

I believe SimpleUrlLogoutSuccessHandler is not clearing the content of the session.
SimpleUrlLogoutSuccessHandler only invokes the handle() method in AbstractAuthenticationTargetUrlRequestHandler and it's Javadoc says:
Invokes the configured RedirectStrategy with the URL returned by the determineTargetUrl method.
The redirect will not be performed if the response has already been committed.
Simplest solution would be to remove this attribute from the session by:
request.getSession().removeAttribute("user");

Related

How to pass attribute to Spring Controller in a stateless application?

I am currently implementing a SAML SSO solution in my application where in my SAMLUserDetailsService, I am loading my user
#Service
public class SAMLUserDetailsServiceImpl implements SAMLUserDetailsService {
#Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
return new User(credential.getNameID().getValue());
}
}
I am then using a SavedRequestAwareAuthenticationSuccessHandler to redirect user to a landing controller upon successful authentication.
#Bean
public AuthenticationSuccessHandler successRedirectHandler() {
SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler =
new SavedRequestAwareAuthenticationSuccessHandler();
successRedirectHandler.setDefaultTargetUrl("/landing");
return successRedirectHandler;
}
Controller:
#RequestMapping("/landing")
public ResponseEntity landing(User user) {
return ResponseEntity.ok(user.getLoginName());
}
Is there a way to pass the User object to my controller. I noticed that this is usually done using a HandlerMethodArgumentResolver but since my application is stateless and does not use sessions, is there a way to achieve this using another way please?
You don't need injection for this. Use following instead:
SecurityContextHolder.getContext().getAuthentication().getName()
or
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
In the latter case check the type of what getPrincipal() returned. It can be String, it can be UserDetails. If latter, cast it to UserDetails and call getUsername().

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.

How to initiate global variable after log out?

My project actual using spring + gradle system,
in some controller file, I added some global variable like this:
#Controller
#RequestMapping(value = "/admin/question")
public class QuestionAdminController {
List<Question> questions;
String message;
Company company;
this is my WebSecurityConfigurerAdapter class:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // temporary disabled for testing file upload
http.headers().frameOptions().disable(); // disabled for h2 console
// Roles permissions
http.authorizeRequests()
// Directories access permissions
.antMatchers("/user/**").access("hasRole('USER')")
.antMatchers("/company/**").access("hasRole('COMPANY')")
.antMatchers("/admin/**").access("hasRole('ADMIN')")
.antMatchers("/db/**").access("hasRole('DBA')")
// All user can access to new routes at the root
.antMatchers("/**").permitAll()
// Other requests need the login
.anyRequest().authenticated()
// Configure the login page
.and().formLogin().loginPage("/login").successHandler(customSuccessHandler).permitAll()
// Default logout from Spring Security
.and().logout().permitAll()
.and()
.sessionManagement()
.maximumSessions(1).expiredUrl("/login?expired");
}
Now when I logging with another user, these global variables keep still the same value.
So I want to know: How can I initiate these global variable after log out?
A controller is not the best place to initialize and store conversational state or user specific data. A Controller by default has a singleton scope hence its shared by all threads servicing user requests.
You need to extract the user specific data into a separate class which has a session scope in the application context. The bean is initialized every time a new user session is initiated. An example using Java config
public class UserData{
List<Questions> questions;
String message;
// etc
}
//in your #Configuration class
#Bean
#Scope("session")
#ScopedProxy
public UserData userData(){
//initialize and return user data
}
In your controller inject UserData as follows
#Controller
#RequestMapping(value = "/admin/question")
public class QuestionAdminController {
#Autowired
private UserData userData;
}
NB.Your UserData bean needs to be a scoped proxy because the controller into which its injected has a longer scope.Hence the proxy has the responsibility of looking up the bean from the session or creating it if it does not exist
If you declare UserData a session scoped, then the #PostConstruct & #PreDestroy annotated methods will be executed when the session is created or destroyed. There you can manipulate global pareameters.
Might not be the best design however, but I need more details about what you want to achieve.

Spring - Call a Service method in JSTL

I'm using Spring Security to handle user authentication for my Spring MVC web app. I'm able to get the username from the Authentication object, but my username is the email address, and I want to be able to show the user's actual name in my header.
So I have my custom User class:
class Users{
String name;
String email;
String password;
// getters and setters
}
I thought about using an aop scoped proxy to set the User in the session, as explained in this blog: http://richardchesterwood.blogspot.co.uk/2011/03/using-sessions-in-spring-mvc-including.html . The problem I faced using this approach is that the AuthenticationSuccessHandler is actually a Service and should be stateless. So Spring doesn't autowire a Users object for me in the Service.
So I created a Service method that would get the username (or email) from the Authentication object and return my Users object. This I can use in my Controllers.
#Service
#Transactional
public class UserServiceImpl implements UserService {
#Override
public Users getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User userD = (User)auth.getPrincipal();
Users currentUser = getUserByEmail(userD.getUsername());
return currentUser;
}
}
So is there a way that I can call this Service method from JSTL to get the user's full name, which I can display in my header?
Am also open to suggestions for a better way to implement this.
EDIT:
In my earlier approach using the AuthenticationSuccessHandler, my code goes like this:
#Service("userDetailsService")
#Transactional
public class UserAuthenticationServiceImpl implements AuthenticationSuccessHandler {
#Autowired
Users currentUser;
#Override
public void onAuthenticationSuccess(HttpServletRequest hsr, HttpServletResponse hsr1, Authentication a) throws IOException, ServletException {
User user = (User) a.getPrincipal();
Users user1 = userDao.getUserByEmail(user.getUsername());
currentUser.setName(user1.getName());
currentUser.setUserRoles(user1.getUserRoles());
//currentUser = user1;
}
}
And in my spring-servlet.xml file, I have this:
<bean id="currentUser" class="com.foo.bean.Users" scope="session">
<!-- this next element effects the proxying of the surrounding bean -->
<aop:scoped-proxy/>
</bean>
The problem I'm facing here is that Spring isn't autowiring my currentUser object because the Service isn't in the session scope.
If the only thing you need is the full name just use an AuthenticationSuccessHandler to retrieve the user and add the name to the session (or the full user if you need more then that).
#Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication auth) throws IOException, ServletException {
User user = (User) auth.getPrincipal();
Users user1 = userDao.getUserByEmail(user.getUsername());
WebUtils.setSessionAttribute(req, "currentUser" user1);
}
Then in your JSP the only thing you need is ${currentUser.username}.
Although I wouldn't suggest stuffing the full user in the session I would suggest just adding the information needed.
WebUtils.setSessionAttribute(req, "currentUsername" user1.getUsername());
Then in your JSP ${currentUsername} saves you a lot of serialization overhead of the session.

Check the state validity of a Spring proxied bean without try-catch

I have a bean being created by a service with the following class:
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
System.out.println(request.getRemoteAddr());
return new Access();
}
}
Everything works as expected, except that when the application is starting, this method is being called, probably because I have some other singleton beans that use the Access bean. At the start up there is no request bound to the Thread, and it's expected to get a java.lang.IllegalStateException when trying to access any property of the request parameter.
No problem. The question is, is it possible to check if the underlying HttpServletRequest of the proxy request is null before calling a property that raises the exception?
You probably want to take a look at RequestContextHolder#getRequestAttributes(). That will return null if you're not currently in a context where request scope could be used.
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
if (RequestContextHolder.getRequestAttributes() != null) {
System.out.println(request.getRemoteAddr());
}
return new Access();
}
}
I think the issue here is with separation of concerns. Usually your service layer should not have any dependency on the servlet classes. This is very much a controller/UI concern.
Your service class should be provided with the properties which it needs to do its job. In this case a String. This service method should be called from a controller method which is injected with the servlet request.
Something like the following:
#Controller
public class MyController {
#Autowired
private AccessManager accessManager;
#RequestMapping
public void handleRequest(HttpServletRequest request) {
accessManager.create(request.getRemoteAddr());
}
}
and your service would then look like this:
#Service
public class AccessManager {
public Access create(String remoteAddress) {
return new Access();
}
}
To sum up, anything annotated as #Service shouldn't have access to the Request.

Resources