Loading Properties with same prefix in Spring Security using Spring Boot - spring-boot

I am authenticating an application with a login page using Spring Seurity and Spring Boot. I am storing user credentials in a properties file (until we finalize a Database) with password in encrypted for starting 5 users.
Here is my properties file
User.properties
my.web.user[0].username=John
my.web.user[0].password=$2y$12$V
my.web.user[0].role=ADMIN,USER
my.web.user[1].username=Johny
my.web.user[1].password=$2y$12$5C
my.web.user[1].role=ADMIN,USER
my.web.user[2].username=McCain
my.web.user[2].password=$2y$12$ERL8mf5.
my.web.user[2].role=USER
So now we can add as many users we want but in this array format. Now, I am loading this properties file in Spring Boot.
MyWebApplication.java
#SpringBootApplication
#ComponentScan(basePackages = { "com.myorg.module" })
#EnableConfigurationProperties(UserConfig.class)
public class MyWebApplication {
public static void main(String[] args) {
ApplicationContext appCtxt = SpringApplication.run(MyWebApplication.class, args);
}
}
UserConfig.java
#Configuration
#PropertySource("user.properties")
#ConfigurationProperties(prefix="my.web")
public class UserConfig {
private List<User> user;
/**
* #return the user
*/
public List<User> getUser() {
return user;
}
/**
* #param user the user to set
*/
public void setUser(List<User> user) {
this.user = user;
}
}
UserDetailsServiceImpl
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User.UserBuilder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import UserConfig;
import User;
#Component
public class UserDetailsServiceImpl implements UserDetailsService{
#Autowired
private UserConfig userConfig;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = findUserByUsername(username);
UserBuilder userBuilder = null;
if(user != null) {
userBuilder = org.springframework.security.core.userdetails.User.withUsername(username);
userBuilder.password(new BCryptPasswordEncoder().encode(user.getPassword()));
userBuilder.roles(user.getRoles());
}else {
throw new UsernameNotFoundException("User Not Found");
}
return userBuilder.build();
}
private User findUserByUsername(String username) {
System.out.println(userConfig);
return userConfig.getUser().get(0);
}
}
Now when I am loading the UserConfig into my UserDetailsServiceImpl using #Autowired. In this case, I am getting userConfig as null. However, when I am putting debug points into getter/setter in UserConfig. I can see that it is populating object with properties contents on Container startup. But later on it vanishes and make it NULL.
Can anyone let me know where am I making mistake?
Thanks in advance.

There are couple of learnings which I had on this.
WebSecurityConfigurerAdapter subclass - This child class should have #Autowired the UserDetailsService interface. In my case, I wrote UserDetailsServiceImpl which is the implementation of UserDetailsService interface.
Properties' array key name must match with the variable name of the #Configuration class. In my case, in properties, I had
my.web.user[0].username=John
and the variable name must be the same in #Configuration class like
#Configuration
#ConfigurationProperties(prefix="my.web")
#PropertySource("user.properties")
public class UserConfig {
private List<User> **user**;
And then it suddenly started working.
P.S. - I did lot of googling and research but nothing worked. Then I read this document 3 times to get it worked.

Related

How to implement the GET request using ServiceLocatorFactoryBean ( Factory Method design Pattern)

I thank you ahead for your time to read my request. I'm new to the Spring Service Locator Factory Method design Pattern and I don't understand the approach behind it. However I followed a turtorial and have been able to implement the Post request for my user registratio spring maven application. My src/main/java folder cointains this five packages:
Config
Controller
Model
Registry
Service
The Config package is to centralize the creation of users and its java class is as bellow:
package com.nidservices.yekoregistration.config;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.ServiceLocatorFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.nidservices.yekoregistration.registry.ServiceRegistry;
#Configuration
public class UserConfig {
#Bean
public FactoryBean<?> factoryBean() {
final ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(ServiceRegistry.class);
return bean;
}
}
The Registry package is to adapt the service base on the type of entity to create and is as bellow:
package com.nidservices.yekoregistration.registry;
public interface AdapterService<T> {
public void process(T request);
}
package com.nidservices.yekoregistration.registry;
public interface ServiceRegistry {
public <T> AdapterService<T> getService(String serviceName);
}
The Service package contains the different types of entity that inherit the User Model and the User Model is as bellow:
public class User implements Serializable {
private UUID id;
private String userIdentifier;
private String userType;
public String getUserIdentifier() {
return userIdentifier;
}
public void setUserIdentifier(String userIdentifier) {
this.userIdentifier = userIdentifier;
}
public String getUserType() {
return userType;
}
public void setUserType(String userType) {
this.userType = userType;
}
#Override
public String toString() {
return "User [userIdentifier=" + userIdentifier + ", UserType=" + userType + "]";
}
}
And the Post Request defined in the Controller is as bellow:
package com.nidservices.yekoregistration.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.nidservices.yekoregistration.model.User;
import com.nidservices.yekoregistration.registry.ServiceRegistry;
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
private ServiceRegistry serviceRegistry;
#PostMapping
public void processStudentDetails(#RequestBody User user) {
serviceRegistry.getService(user.getUserType()).process(user);
}
}
Now I'm struggling to make the GET Request to get all created users. I'm used with the DAO design pattern and very new with the concept behind ServiceLocatorFactoryBean. I appreciate your help to help me implement my CRUD endpoints using ServiceLocatorFactoryBean. Thanks in advance.

Using transactional in spring boot with hibernate

I am getting the error while using hibernate in spring boot application No qualifying bean of type TransactionManager' available
I am using the following config class:
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#org.springframework.context.annotation.Configuration
#EnableTransactionManagement
public class Config {
#Bean
public SessionFactory sessionFactory() {
Configuration configuration = new Configuration();
configuration.configure();
configuration.addAnnotatedClass(Ct.class);
configuration.addAnnotatedClass(St.class);
SessionFactory sessionFactory = configuration.buildSessionFactory();
return sessionFactory;
}
}
#RestController
public class RestAPIController {
#Autowired
private SessionFactory sessionFactory;
#PutMapping("/addS")
#Transactional
public void addSt(#RequestParam("cc") String cc,#RequestParam("st") String st) {
CC cc1= new CC();
CC.setCode(cc);
State state = new State(cc,st);
sessionFactory.getCurrentSession().save(state);
}
}
}
The main reason I added the #Transactional in the addSt method is due to error: The transaction was still an active when an exception occurred during Database.
So I turned to use spring boot for managing transactions. I am not sure what to do here.
--------------------UPDATED CODE--------------------
#Repository
public interface StateRepository extends CrudRepository<State, String> {}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.ArrayList;
import java.util.List;
#Service
#Transactional
public class StateService {
#Autowired
private StateRepository stateRepository;
public void save(State state) {
stateRepository.save(state);
}
public List<State> findAll() {
List<State> states = new ArrayList<>();
stateRepository.findAll().forEach(states::add);
return states;
}
}
For starters use proper layers and write a service and use JPA instead of plain Hibernate. If you want a Session you can always use EntityManager.unwrap to obtain the underlying Session.
#Service
#Transactional
public StateService {
#PersistenceContext
private EntityManager em;
public void save(State state) {
em.persist(state);
}
Use this service in your controller instead of the SessionFactory.
#RestController
public class RestAPIController {
private final StateService stateService;
RestAPIController(StateService stateService) {
this.stateService=stateService;
}
#PutMapping("/addS")
public void addSt(#RequestParam("cc") String cc, #RequestParam("st") String st) {
CC cc1= new CC();
CC.setCode(cc);
State state = new State(cc,st);
stateService.save(state);
}
}
Now ditch your Config class and restart the application.
NOTE
When using Spring Data JPA it is even easier, define a repository extending CrudRepository and inject that into the service instead of an EntityManager. (I'm assuming that Long is the type of primary key you defined).
public interface StateRepository extends CrudRepository<State, Long> {}
#Service
#Transactional
public StateService {
private final StateRepository states;
public StateService(StateRepository states) {
this.states=states;
}
public void save(State state) {
states.save(state);
}
}

Authentication issue in Spring Security (checking only username not password?)

this is my first project with Spring and I have just started to create the login with Spring Security. I want some pages to be accessible only for the admin and not for the players. I've found some examples on the web and this mechanism works pretty well, I have this secured page that is protected by the login and it's forbidden when the user has no ROLE_ADMIN.
#PreAuthorize("hasAuthority('ROLE_ADMIN')")
#GetMapping("/secured/all")
public String securedHello() {
return "Secured Hello";
}
The problem is that testing my code I found out that Spring authenticates the admin (and the user as well) only checking the username. If I put the wrong password it allows me to enter anyway. I don't understand how this is possible, shouldn't Spring Security do all the authentication work by itself? I've seen somebody suggested to implement an authentication manager or something like that, but I don't understand why and how to insert it in my code. I'm stuck on this since two days, please any advice wuold be really appreciated.
These are my classes:
package model;
import java.io.IOException;
import javax.naming.AuthenticationException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.fasterxml.jackson.databind.ObjectMapper;
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#EnableJpaRepositories(basePackageClasses = PlayersRepository.class)
#ComponentScan(basePackageClasses= CustomUserDetailsService.class)
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(getPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
//http.csrf().disable();
http.authorizeRequests()
.antMatchers("**/secured/**").access("hasAuthority('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin().permitAll();
}
private PasswordEncoder getPasswordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
#Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
};
}
}
package model;
import java.util.ArrayList;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private PlayersRepository usersRepository;
#Autowired
private RoleRepository rolesRepository;
public CustomUserDetailsService(PlayersRepository usersRepository, RoleRepository rolesRepository) {
this.usersRepository=usersRepository;
this.rolesRepository=rolesRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Player> optionalUser = usersRepository.findByUsername(username);
optionalUser
.orElseThrow(() -> new UsernameNotFoundException("Username not found"));
Player user= optionalUser.get();
System.out.println(user);
return toUserDetails(new UserObject(user.getUsername(),user.getPassword(),user.getRole()));
}
private UserDetails toUserDetails(UserObject userObject) {
return User.withUsername(userObject.name)
.password(userObject.password)
.roles(userObject.role).build();
}
private static class UserObject {
private String name;
private String password;
private String role;
public UserObject(String name, String password, String role) {
this.name = name;
this.password = password;
this.role = role;
}
}
}
package model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class CustomUserDetails extends Player implements UserDetails {
String role;
public CustomUserDetails(final Player user) {
super(user);
}
public CustomUserDetails(Optional<Player> user, String role) {
super(user);
this.role=role;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
list.add(new SimpleGrantedAuthority("ROLE_"+ role));
System.out.println(list);
return list;
}
#Override
public String getPassword() {
return super.getPassword();
}
#Override
public String getUsername() {
return super.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
Shouldn't Spring Security do all the authentication work by itself?
Yes, Spring Security does that for you using an AuthenticationManager.
I've seen somebody suggested to implement an authentication manager or something like that, but I don't understand why and how to insert it in my code.
You actually already have an AuthenticationManager, since you built one within the configure() method:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
}
So, what is exactly the reason this isn't working you may ask. Well, the AuthenticationManager you provided contains two parts:
A part that fetches the user information (CustomUserDetailsService)
Another part that checks the password (the getPasswordEncoder()).
What happens behind the screens is that Spring calls your CustomUserDetailsService to fetch your user information, including your (hashed) password. After fetching that information, it calls your PasswordEncoder.matches() function to verify if the raw entered password matches your hashed password provided by the CustomUserDetailsService.
In your case, your PasswordEncoder.matches() function looks like this:
#Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
This means that regardless of what password you provide, it will return true. This is exactly what you're experiencing since any password will work.
So, how do you solve this? Well, your PasswordEncoder should actually hash your raw password and compare it to the hashed password that is being passed, for example:
#Override
public boolean matches(CharSequence rawPassword, String hashedPassword) {
String hashedPassword2 = null; // hash your rawPassword here
return hashedPassword2.equals(hashedPassword);
}
The implementation of this method depends on how you store your password in your database. Spring Security already comes with a few implementation including BcryptPasswordEncoder, StandardPasswordEncoder, MessageDigestPasswordEncoder, ... . Some of these implementations are deprecated, mostly to indicate that the hashing mechanisms used by those encoders are considered unsafe. There are no plans at the moment of writing to remove those encoders, as mentioned by the Javadoc:
Digest based password encoding is not considered secure. Instead use an adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or SCryptPasswordEncoder. Even better use DelegatingPasswordEncoder which supports password upgrades. There are no plans to remove this support. It is deprecated to indicate that this is a legacy implementation and using it is considered insecure.
(Emphasis is my own)
If you are free to choose which implementation you pick, then Spring recommends using BCryptPasswordEncoder as mentioned by the Javadoc:
Service interface for encoding passwords. The preferred implementation is BCryptPasswordEncoder.
I just had a Quick scan I found this
private PasswordEncoder getPasswordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
#Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
};
}
In your matches you are returning always true.
I guess here you should put logic for checking password for equality something like this
#Override
public boolean matches(CharSequence charSequence, String s) {
return charSequence.toString.equals(s);
}
I would suggest you use something like this
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

Update users informations during login against LDAP AD using Spring

I'm getting the users of my web-app from the Acive Directory.
So I created a custom UserDetailsContextMapper to save some data of the user to the web-app's MySql Database.
And this is my security configuration about Ldap:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth
.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("myDomain.local", "ldap://LDAP_IP:389/");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setUserDetailsContextMapper(userDetailsContextMapper());
return provider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Bean
public UserDetailsContextMapper userDetailsContextMapper() {
return new LdapUserDetailsContextMapper();
}
I would like to know when and if the data on the AD are changed from last login.
For example if today at 10:00AM I was member of group A inside the AD and now I'm member of group A and B, I would like to update the authorities on MySql.
Is there a field or something inside AD to know that?
EDIT:
I would like to check if something change for a particulare user during the login phase, in this way I can update the information on MySql.
To find when a user was last modified, you can use the "whenchanged" attribute.
if you extend LdapUserDetailsMapper, and override the mapUserFromContext, it might look like this:
package example.active.directory.authentication;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Collection;
public class CustomUserMapper extends LdapUserDetailsMapper{
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities){
UserDetails details = super.mapUserFromContext(ctx, username, authorities);
String[] changedValues = ctx.getStringAttributes("whenchanged");
if(changedValues != null && changedValues.length > 0){
LocalDateTime lastChangedTime = Arrays.stream(changedValues)
.map(input ->
OffsetDateTime.parse(
input,
DateTimeFormatter.ofPattern("uuuuMMddHHmmss[,S][.S]X")
).toLocalDateTime()
)
.sorted((a, b) -> a.compareTo(b) * -1)
.findFirst()
.orElse(null);
System.out.println(lastChangedTime);
//Do something with value?
}
return details;
}
}

How do I get the Session Object in Spring?

I am relatively new to Spring and Spring security.
I was attempting to write a program where I needed to authenticate a user at the server end using Spring security,
I came up with the following:
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider{
#Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
throws AuthenticationException
{
System.out.println("Method invoked : additionalAuthenticationChecks isAuthenticated ? :"+usernamePasswordAuthenticationToken.isAuthenticated());
}
#Override
protected UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException
{
System.out.println("Method invoked : retrieveUser");
//so far so good, i can authenticate user here, and throw exception if not authenticated!!
//THIS IS WHERE I WANT TO ACCESS SESSION OBJECT
}
}
My usecase is that when a user is authenticated, I need to place an attribute like:
session.setAttribute("userObject", myUserObject);
myUserObject is an object of some class that I can access throughout my server code across multiple user requests.
Your friend here is org.springframework.web.context.request.RequestContextHolder
// example usage
public static HttpSession session() {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getRequest().getSession(true); // true == allow create
}
This will be populated by the standard spring mvc dispatch servlet, but if you are using a different web framework you have add org.springframework.web.filter.RequestContextFilter as a filter in your web.xml to manage the holder.
EDIT: just as a side issue what are you actually trying to do, I'm not sure you should need access to the HttpSession in the retieveUser method of a UserDetailsService. Spring security will put the UserDetails object in the session for you any how. It can be retrieved by accessing the SecurityContextHolder:
public static UserDetails currentUserDetails(){
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null) {
Object principal = authentication.getPrincipal();
return principal instanceof UserDetails ? (UserDetails) principal : null;
}
return null;
}
Since you're using Spring, stick with Spring, don't hack it yourself like the other post posits.
The Spring manual says:
You shouldn't interact directly with the HttpSession for security
purposes. There is simply no justification for doing so - always use
the SecurityContextHolder instead.
The suggested best practice for accessing the session is:
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
The key here is that Spring and Spring Security do all sorts of great stuff for you like Session Fixation Prevention. These things assume that you're using the Spring framework as it was designed to be used. So, in your servlet, make it context aware and access the session like the above example.
If you just need to stash some data in the session scope, try creating some session scoped bean like this example and let autowire do its magic. :)
i made my own utils. it is handy. :)
package samples.utils;
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.MessageSource;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.ui.context.Theme;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ThemeResolver;
import org.springframework.web.servlet.support.RequestContextUtils;
/**
* SpringMVC通用工具
*
* #author 应卓(yingzhor#gmail.com)
*
*/
public final class WebContextHolder {
private static final Logger LOGGER = LoggerFactory.getLogger(WebContextHolder.class);
private static WebContextHolder INSTANCE = new WebContextHolder();
public WebContextHolder get() {
return INSTANCE;
}
private WebContextHolder() {
super();
}
// --------------------------------------------------------------------------------------------------------------
public HttpServletRequest getRequest() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attributes.getRequest();
}
public HttpSession getSession() {
return getSession(true);
}
public HttpSession getSession(boolean create) {
return getRequest().getSession(create);
}
public String getSessionId() {
return getSession().getId();
}
public ServletContext getServletContext() {
return getSession().getServletContext(); // servlet2.3
}
public Locale getLocale() {
return RequestContextUtils.getLocale(getRequest());
}
public Theme getTheme() {
return RequestContextUtils.getTheme(getRequest());
}
public ApplicationContext getApplicationContext() {
return WebApplicationContextUtils.getWebApplicationContext(getServletContext());
}
public ApplicationEventPublisher getApplicationEventPublisher() {
return (ApplicationEventPublisher) getApplicationContext();
}
public LocaleResolver getLocaleResolver() {
return RequestContextUtils.getLocaleResolver(getRequest());
}
public ThemeResolver getThemeResolver() {
return RequestContextUtils.getThemeResolver(getRequest());
}
public ResourceLoader getResourceLoader() {
return (ResourceLoader) getApplicationContext();
}
public ResourcePatternResolver getResourcePatternResolver() {
return (ResourcePatternResolver) getApplicationContext();
}
public MessageSource getMessageSource() {
return (MessageSource) getApplicationContext();
}
public ConversionService getConversionService() {
return getBeanFromApplicationContext(ConversionService.class);
}
public DataSource getDataSource() {
return getBeanFromApplicationContext(DataSource.class);
}
public Collection<String> getActiveProfiles() {
return Arrays.asList(getApplicationContext().getEnvironment().getActiveProfiles());
}
public ClassLoader getBeanClassLoader() {
return ClassUtils.getDefaultClassLoader();
}
private <T> T getBeanFromApplicationContext(Class<T> requiredType) {
try {
return getApplicationContext().getBean(requiredType);
} catch (NoUniqueBeanDefinitionException e) {
LOGGER.error(e.getMessage(), e);
throw e;
} catch (NoSuchBeanDefinitionException e) {
LOGGER.warn(e.getMessage());
return null;
}
}
}
Indeed you can access the information from the session even when the session is being destroyed on an HttpSessionLisener by doing:
public void sessionDestroyed(HttpSessionEvent hse) {
SecurityContextImpl sci = (SecurityContextImpl) hse.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
// be sure to check is not null since for users who just get into the home page but never get authenticated it will be
if (sci != null) {
UserDetails cud = (UserDetails) sci.getAuthentication().getPrincipal();
// do whatever you need here with the UserDetails
}
}
or you could also access the information anywhere you have the HttpSession object available like:
SecurityContextImpl sci = (SecurityContextImpl) session().getAttribute("SPRING_SECURITY_CONTEXT");
the last assuming you have something like:
HttpSession sesssion = ...; // can come from request.getSession(false);
I try with next code and work excellent
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Created by jaime on 14/01/15.
*/
#Controller
public class obteinUserSession {
#RequestMapping(value = "/loginds", method = RequestMethod.GET)
public String UserSession(ModelMap modelMap) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName();
modelMap.addAttribute("username", name);
return "hellos " + name;
}
In my scenario, I've injected the HttpSession into the CustomAuthenticationProvider class
like this
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider{
#Autowired
private HttpSession httpSession;
#Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
throws AuthenticationException
{
System.out.println("Method invoked : additionalAuthenticationChecks isAuthenticated ? :"+usernamePasswordAuthenticationToken.isAuthenticated());
}
#Override
protected UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException
{
System.out.println("Method invoked : retrieveUser");
//so far so good, i can authenticate user here, and throw exception
if not authenticated!!
//THIS IS WHERE I WANT TO ACCESS SESSION OBJECT
httpSession.setAttribute("userObject", myUserObject);
}
}
If all that you need is details of User, for Spring Version 4.x you can use #AuthenticationPrincipal and #EnableWebSecurity tag provided by Spring as shown below.
Security Configuration Class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
Controller method:
#RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(#AuthenticationPrincipal User user) {
...
}
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
attr.getSessionId();

Resources