Spring: Get Custom User Object from SecurityContextHolder - java

I try to implement log-file which stores all logins.
So far I put some code to my LoginHandler but I always get the ERROR:
org.springframework.security.core.userdetails.User cannot be cast to at.qe.sepm.asn_app.models.UserData
The method in my LoginHandler:
#Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
UserData user = (UserData)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
AuditLog log = new AuditLog(user.getUsername() + " [" + user.getUserRole() + "]" ,"LOGGED IN", new Date());
auditLogRepository.save(log);
handle(httpServletRequest, httpServletResponse, authentication);
clearAuthenticationAttributes(httpServletRequest);
}
Is it possible to change the return value type from SecurityContextHolder to my UserData object?
Additional Code:
public class MyUserDetails implements UserDetails {
private UserData user;
public UserData getUser(){
return user;
}
#Override
public String getUsername(){
return user.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return false;
}
#Override
public boolean isAccountNonLocked() {
return false;
}
#Override
public boolean isCredentialsNonExpired() {
return false;
}
#Override
public boolean isEnabled() {
return false;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
#Override
public String getPassword(){
return user.getPassword();
}
}
MyUserDetails myUserDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserData user = myUserDetails.getUser();
The compiler says that UserDetails and MyUserDetails are incompatible types.
My WebSecurityConfig:
#Configuration
#EnableWebSecurity()
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.headers().frameOptions().disable(); // needed for H2 console
http.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.invalidateHttpSession(false)
.logoutSuccessUrl("/login.xhtml");
http.authorizeRequests()
//Permit access to the H2 console
.antMatchers("/h2-console/**").permitAll()
//Permit access for all to error pages
.antMatchers("/error/**")
.permitAll()
// Only access with admin role
.antMatchers("/admin/**")
.hasAnyAuthority("ADMIN")
//Permit access only for some roles
.antMatchers("/secured/**")
.hasAnyAuthority("ADMIN", "EMPLOYEE", "PARENT")
//If user doesn't have permission, forward him to login page
.and()
.formLogin()
.loginPage("/login.xhtml")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/secured/welcome.xhtml").successHandler(successHandler());
// :TODO: user failureUrl(/login.xhtml?error) and make sure that a corresponding message is displayed
http.exceptionHandling().accessDeniedPage("/error/denied.xhtml");
http.sessionManagement().invalidSessionUrl("/error/invalid_session.xhtml");
}
#Bean
public AuthenticationSuccessHandler successHandler() {
return new LoginHandler();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//Configure roles and passwords via datasource
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username, password, true from user_data where username=?")
.authoritiesByUsernameQuery("select username, user_role from user_data where username=?")
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder(){
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
}
I also tried to implement the Springs User, UserDetails and UserDetailsService but I failed so far. I have no idea how to adjust these to my project because I use inheritance. My models are UserData which inherits to Parent and Employee. So I also have UserBaseRepository and UserDataRepository. These all confuses me a lot.
For now I stuck in implementing the methods from the Spring provided User-classes.

The org.springframework.security.core.UserDetails should always be implemented by your own UserData or another class that wraps your UserData instance
For example:
public class UserData{
private username;
private password;
/// other user parameters
.
.
etc
}
public class MyUserDetails implements UserDetails {
private UserData user;
public UserData getUser(){
return user;
}
#Override
public String getUsername(){
return user.getUsername();
}
#Override
public String getPassword(){
return user.getPassword();
}
}
And then you cast it like this
MyUserDetails myUserDetails = (MyUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserData user = myUserDetails.getUser();

Related

Error: User doesn't exist with this username: when using spring security

I'm building backend using Spring Boot + Spring Security and testing it with Postman.
For frontend I will use Android app.
Implemented http basic auth.
When I click on the Authentication tab in Postman and choose Basic Auth from the drop down box and then I enter username and password fields there it works just fine. But when I am sending raw JSON request body I am getting following error:
org.springframework.security.authentication.InternalAuthenticationServiceException: User doesn't exist with this username:
I assume it is caused with this class
CustomUserDetailsService.java
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
CustomUserDetails userDetails = null;
if(user != null){
userDetails = new CustomUserDetails();
userDetails.setUser(user);
}else{
throw new UserNotFoundException("User doesn't exist with this username: " + username);
}
return userDetails;
}
}
But I am not sure why as it works with Postman auth drop down box.
Here is more code that I use:
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(encodePWD());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable();
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/rest/**").permitAll()
.and()
.authorizeRequests()
.antMatchers("/secure/**").hasAnyRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll();
}
#Bean
public BCryptPasswordEncoder encodePWD() {
return new BCryptPasswordEncoder();
}
}
CustomUserDetailsService.java
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
CustomUserDetails userDetails = null;
if(user != null){
userDetails = new CustomUserDetails();
userDetails.setUser(user);
}else{
throw new UserNotFoundException("User doesn't exist with this username: " + username);
}
return userDetails;
}
}
CustomUserDetails.java
#Getter
#Setter
public class CustomUserDetails implements UserDetails {
private User user;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getRoles().stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRole())).collect(Collectors.toSet());
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.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;
}
}
Also I am using this to login user and to check if matches one in DB.
#PostMapping("/loginuser")
ResponseEntity<Object> login(#RequestBody User user) {
List<User> userList = userRepository.findByUsernameAndPassword(user.getUsername(), user.getPassword());
if (userList.size() != 1) {
throw new UserNotFoundException("Entity not found");
} else {
return new ResponseEntity<>(userList.get(0), HttpStatus.OK);
}
}
Is it even possible to use https basic auth for rest server api?
Or do I have to use some authentications with tokens like OAuth and similar?
UPDATE
If you use the Postman Authorization tab, Postman constructs the corresponding Authorization header Authorization: Basic <credentials>, where credentials is the Base64 encoding of username and password.
You can see this header in your Postman request clicking headers and hidden.
This header is needed for Spring Security HTTP basic authentication. If you just post username and password in the body of the request as JSON, Spring Security HTTP basic authentication cannot extract the username from the header. Therefore, user equals null and the exception is thrown.

Spring Security - Active Directory Get Custom User details

I am having a challenge extracting original user details from the LdapUserDetailsImpl such as the getUsername() returns null
I have the following Java classes
Custom User Class
public class AppUserDetails extends LdapUserDetailsImpl {
public AppUserDetails() {
super();
}
private String mail; //mail
}
Custom User Details Mapper
public class AppUserDetailsContextMapper extends LdapUserDetailsMapper {
public AppUserDetailsContextMapper() {
super();
}
#Override
public AppUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
UserDetails details = super.mapUserFromContext(ctx, username, authorities);
String mail = ctx.getStringAttribute("mail");
AppUserDetails appUserDetails = new AppUserDetails();
appUserDetails.setMail(mail);
return appUserDetails;
}
}
Web security configuration
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
Environment env;
#Bean
public UserDetailsContextMapper userDetailsContextMapper() {
return new AppUserDetailsContextMapper();
}
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider =
new ActiveDirectoryLdapAuthenticationProvider(
env.getRequiredProperty("spring.ldap.domain"),
env.getRequiredProperty("spring.ldap.urls")
);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setUserDetailsContextMapper(userDetailsContextMapper());
return provider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(activeDirectoryLdapAuthenticationProvider())
.userDetailsService(userDetailsService());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(
Collections
.singletonList(activeDirectoryLdapAuthenticationProvider())
);
}
}
However I am having a serious challenge trying to getting Custom User Details in the controller:
#Controller
public class HomeController {
#GetMapping(value = {"/home"})
public String home(Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println(authentication.getPrincipal());
AppUserDetails appUserDetails = (AppUserDetails) authentication.getPrincipal();
System.out.println(appUserDetails);
return "home";
}
}
I am getting a NullPointer exception if I try to any property from the LdapUserDetailsImpl class. However, I am accurately getting all the properties from the AppUserDetails - which extends the LdapUserDetailsImpl class.
Where might I be missing it?

Spring custom auth filter and custom jwtfilter

What I'm trying to implement is to make my multitenant app workspace aware. With this I mean that besides username and password, I'm validating the workspace as well.
Before, I had (working) normal authentication (username and password) and a JWTFilter that is a OncePerRequestFilter.
What I did?
Extended UsernamePasswordAuthenticationToken: just to add the workspace
Extended AbstractUserDetailsAuthenticationProvider: defining my customPasswordEncoder and customUserDetailsService
Made a CustomUserDetailsService: instead of the loadByUsername I've made a loadUserByWorkspaceAndUsername
Configured the WebSecurity for the new extended classes
The outcome is always unauthorized :(
What I've tried?
While debugging the code never pass on the CustomAuthenticationFilter and that's the reason I'm focusing my efforts there. Really doesn't know what I'm doing wrong here. If you need any further information please shout.
Replacing the UsernamePasswordAuthenticationFilter using the addFilter(authenticationFilter())
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(authenticationFilter(), JwtFilter.class);
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(authenticationFilter(),UsernamePasswordAuthenticationFilter.class);
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jwtAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
A bit of code.
CustomAuthenticationToken
public class CustomAuthenticationToken extends UsernamePasswordAuthenticationToken {
private String workspace;
public CustomAuthenticationToken(final Object principal,
final Object credentials,
final String workspace) {
super(principal, credentials);
this.workspace = workspace;
}
public CustomAuthenticationToken(final Object principal,
final Object credentials,
final String workspace, Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
this.workspace = workspace;
super.setAuthenticated(true);
}
public String getWorkspace() {
return this.workspace;
}
}
CustomAuthenticationFilter
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "workspace";
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: "
+ request.getMethod());
}
CustomAuthenticationToken authRequest = getAuthRequest(request);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
private CustomAuthenticationToken getAuthRequest(HttpServletRequest request) {
String username = obtainUsername(request);
String password = obtainPassword(request);
String domain = obtainDomain(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
if (domain == null) {
domain = "";
}
username = username.trim();
return new CustomAuthenticationToken(username, password, domain);
}
private String obtainDomain(HttpServletRequest request) {
return request.getParameter(SPRING_SECURITY_FORM_DOMAIN_KEY);
}
}
CustomUserDetailsAuthenticationProvider
#Component
public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
/**
* The plaintext password used to perform
* PasswordEncoder#matches(CharSequence, String)} on when the user is
* not found to avoid SEC-2056.
*/
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
private final PasswordEncoder customPasswordEncoder;
private final CustomUserDetailsService customUserDetailsService;
private String userNotFoundEncodedPassword;
public CustomUserDetailsAuthenticationProvider(final PasswordEncoder customPasswordEncoder,
final CustomUserDetailsService customUserDetailsService) {
this.customPasswordEncoder = customPasswordEncoder;
this.customUserDetailsService = customUserDetailsService;
}
#Override
protected void additionalAuthenticationChecks(final UserDetails userDetails,
final UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
final String presentedPassword = authentication.getCredentials().toString();
if (!customPasswordEncoder.matches(presentedPassword, userDetails.getPassword())) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
#Override
protected UserDetails retrieveUser(final String username,
final UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
final CustomAuthenticationToken auth = (CustomAuthenticationToken) authentication;
UserDetails loadedUser;
try {
loadedUser = this.customUserDetailsService.loadUserByWorkspaceAndUsername(auth.getWorkspace(), auth.getPrincipal().toString());
} catch (UsernameNotFoundException notFound) {
if (authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials().toString();
customPasswordEncoder.matches(presentedPassword, userNotFoundEncodedPassword);
}
throw notFound;
} catch (Exception repositoryProblem) {
throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
if (loadedUser == null) {
throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
#Override
protected void doAfterPropertiesSet() throws Exception {
Assert.notNull(this.customUserDetailsService, "A UserDetailsService must be set");
this.userNotFoundEncodedPassword = this.customPasswordEncoder.encode(USER_NOT_FOUND_PASSWORD);
}
}
CustomUserDetailsServiceImpl
#Component
public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
private static final Logger LOGGER = LoggerFactory.getLogger(com.cliwise.security.workspace.CustomUserDetailsServiceImpl.class);
private final LoginAttemptService loginAttemptService;
private final UserRepository userRepository;
private final HttpServletRequest request;
public CustomUserDetailsServiceImpl(LoginAttemptService loginAttemptService, UserRepository userRepository, HttpServletRequest request) {
this.loginAttemptService = loginAttemptService;
this.userRepository = userRepository;
this.request = request;
}
#Override
public UserDetails loadUserByWorkspaceAndUsername(String workspace, String username) throws UsernameNotFoundException {
final User user = userRepository.findByUsernameOrEmailAndWorkspace(username, username, workspace)
.orElseThrow(() -> new UserNotFoundException("User not found with username or email : " + username));
return UserPrincipal.create(user);
}
}
The last but no least
WebSecurity
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private final CustomAuthenticationEntryPoint unauthorizedHandler;
private final CustomUserDetailsAuthenticationProvider customUserDetailsAuthenticationProvider;
public WebSecurity(final CustomAuthenticationEntryPoint unauthorizedHandler,
final CustomUserDetailsAuthenticationProvider customUserDetailsAuthenticationProvider) {
this.unauthorizedHandler = unauthorizedHandler;
this.customUserDetailsAuthenticationProvider = customUserDetailsAuthenticationProvider;
}
#Override
public void configure(final AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.authenticationProvider(customUserDetailsAuthenticationProvider);
}
#Bean
public CustomAuthenticationFilter authenticationFilter() throws Exception {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
#Bean(BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler())
.and()
.addFilterBefore(jwtAuthenticationFilter(), CustomAuthenticationFilter.class);
http
.authorizeRequests()
.antMatchers("/auth").permitAll()
.anyRequest()
.authenticated();
}
#Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
#Bean
public JwtFilter jwtAuthenticationFilter() {
return new JwtFilter();
}
}
Thanks in advance for your time.
My understanding is that you are facing the problem in CustomUserDetailsAuthenticationProvider. Since you are extending AbstractUserDetailsAuthenticationProver you will get a default implementation for
public Authentication authenticate(Authentication authentication)
throws AuthenticationException;
See if its properly authenticating the authentication object, if not you will have to override the method and write your own implementation.

How to authenticate user in spring using AuthenticationManagerBuilder and userDetailsService based on credentials retrived from user entity

i am a newbie to spring
was not able to implement login to the application, actually could not figure out where its wrong.
followed a youtube video to do all this.
Help would be very much appreciated.
When i try to log in the application wont allow to log in.
console logs shows querys are being executed but cant log into the system.
also the password are saved in plain ASCII.
WebSecurityConfig class
#Configuration
#ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccessDeniedHandler accessDeniedHandler;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
// roles admin allow to access /actuator/**
// roles user allow to access /Application/**
// custom 403 access denied handler
#Override
protected void configure(HttpSecurity http) throws Exception {
// some antMatchers permit all
}
}
customUserDetailsService class
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
private final UserRepo userRepo;
#Autowired
public CustomUserDetailsService(UserRepo userRepo) {
this.userRepo = userRepo;
}
#Override
// userId is reffered as username
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user=userRepo.findByUsername(userName);
if(null == user){
System.out.println("\n\n\n No user present with username: "+userName);
throw new UsernameNotFoundException("No user present with username: "+userName);
}else{
CustomUserDetails c =new CustomUserDetails(user);
//System.out.println(c.getAuthorities());
return c;
}
}
}
CustomUserDetails class
public class CustomUserDetails extends User implements UserDetails{
private static final long serialVersionUID = 1L;
public CustomUserDetails(User user){
super(user);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<UserRole> roles = super.getUserRole();
List<String> userRoles= new ArrayList<String>();
for(UserRole r : roles) {
userRoles.add(r.getRole().toString());
}
String strRoles=StringUtils.collectionToCommaDelimitedString(userRoles);
return AuthorityUtils.commaSeparatedStringToAuthorityList(strRoles);
}
#Override
public boolean isAccountNonExpired() {
return super.isAccountNonExpired();
}
#Override
public boolean isAccountNonLocked() {
return super.isAccountNonLocked();
}
#Override
public boolean isCredentialsNonExpired() {
return super.isCredentialsNonExpired();
}
#Override
public boolean isEnabled() {
return super.isEnabled();
}
#Override
public String getUsername() {
return super.getUsername();
}
#Override
public String getPassword() {
return super.getPassword();
}
}

Getting "Maximum sessions of 1 for this principal exceeded" when logging in with different username

I configured my application to allow only single session per account.
It works fine when i use JdbcDaoImpl provider.
It also works fine when i use DaoAuthenticationProvider with custom User object extending spring User.
But when i try to setup spring security with custom User object implementing UsersDetails interface i got the message above when I tried to log in using different account. I cannot figure out why.
Here is my security configuration :
<session-management invalid-session-url="/">
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
<authentication-manager>
<authentication-provider ref="daoAuthenticationProvider"/>
</authentication-manager>
<beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsServiceImplementation"></beans:property>
</beans:bean>
<beans:bean id="userDetailsServiceImplementation" class="com.company.service.implementation.UserServiceImpl" />
And my custom User object :
public class UserVo extends CommonVo implements UserDetails{
private String username;
private String password;
private String firstName;
private String lastName;
private String enabled;
private List<GrantedAuthority> userAuthorities;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEnabled() {
return enabled;
}
public void setEnabled(String enabled) {
this.enabled = enabled;
}
public List<GrantedAuthority> getUserAuthorities() {
return userAuthorities;
}
public void setUserAuthorities(List<GrantedAuthority> userAuthorities) {
this.userAuthorities = userAuthorities;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return userAuthorities;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return "Y".equals(enabled) ? true : false;
}
}
And this is my UserDetailsService implementation :
public class UserServiceImpl implements UserService, UserDetailsService{
#Autowired
private UserDAO userDao;
#Autowired
private UserVo userVo;
#Override
public int insert(UserVo userVo) {
return userDao.insert(userVo);
}
#SuppressWarnings("unchecked")
#Override
public List<UserVo> list(UserVo userVo) {
return (List<UserVo>) userDao.select(userVo);
}
public List<String> listUserRoles(UserVo userVo) {
return (List<String>) userDao.listUserRoles(userVo);
}
#SuppressWarnings("rawtypes")
#Override
public Map select(UserVo userVo) {
return userDao.select(userVo);
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
userVo.setUsername(username);
#SuppressWarnings({ "rawtypes", "unchecked" })
Map result = new HashMap(userDao.select(userVo));
List<String> userRoles = userDao.listUserRoles(userVo);
String sRoles = StringUtils.join(userRoles, ",");
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for(String role : userRoles){
authorities.add(new SimpleGrantedAuthority(role));
}
userVo.setUsername(result.get("username").toString());
userVo.setPassword(result.get("password").toString());
userVo.setEnabled(result.get("enabled").toString());
userVo.setUserAuthorities(authorities);
//return new LoginVo(result.get("username").toString(), result.get("password").toString(), AuthorityUtils.commaSeparatedStringToAuthorityList(sRoles));
return userVo;
}
}
This works as expected if i return object that extends spring User class, in this case LoginVo.
For concurrency control Spring Security uses a SessionRegistry the default implementation uses a HashMap to store things. For a HashMap to work correctly you need to have a correctly implemented hashCode and equals method. If you don't (or always return a default value) it won't work correctly.
To solve simply implement a correct hashCode and equals method in your custom object.
If anyone faces this issue in spring boot ,this is what you have to add in your security config file,Apart From M.Deinum Answer
//security configuration class for implementing spring security on urls
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
//for handling user success handler
#Autowired
private CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;
#Override
//this configuration is for handling user requests
protected void configure(HttpSecurity http) {
try {
http
.authorizeRequests()
.antMatchers("/orders").permitAll()
.antMatchers("/createrole").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/admin/**").hasAuthority("admin")
.antMatchers("/agent/**").hasAuthority("agent")
.antMatchers("/distributor/**").hasAuthority("distributor")
.antMatchers("/home/**").hasAuthority("user").anyRequest()
.authenticated().and().csrf().disable().formLogin().successHandler(customizeAuthenticationSuccessHandler)
.loginPage("/login").failureUrl("/login?error=true")
.usernameParameter("username")
.passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/logout.done").deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.logoutSuccessUrl("/login").and().exceptionHandling().accessDeniedPage("/403");
http.sessionManagement( ).maximumSessions(1). maxSessionsPreventsLogin(true);
http.sessionManagement( ).sessionFixation( ).migrateSession( )
.sessionAuthenticationStrategy( registerSessionAuthStr( ) );
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("Exception here");
}
}
//this method allows static resources to be neglected by spring security
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**","/assets/**","/fonts/**","/dis/**","/vendor1/**","/mobile/**");
}
#Bean
public SessionRegistry sessionRegistry( ) {
SessionRegistry sessionRegistry = new SessionRegistryImpl( );
return sessionRegistry;
}
#Bean
public RegisterSessionAuthenticationStrategy registerSessionAuthStr( ) {
return new RegisterSessionAuthenticationStrategy( sessionRegistry( ) );
}
///Very important ,you wont login again after logout if you dont include this
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
//BCryptPasswordEncoder encoder = passwordEncoder();
try {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
} catch (Exception e) {
System.out.println("Login Failed");
}
}
}
Ok, i've solved my problem by following #m-deinum answer in the comment section of my original question.
What i did is generate hashCode() and equals() by right clicking eclipse -> Source -> Generate hashCode() and equals() in both UserVo and CommonVo.
It turns out that i have to override both method above if i have my own implementation of UsersDetail interface
I'm developing a normal spring application [Configured without xml] without any boot Configurations...
i overrided my hascode and equal methods in the customer.
I not able to login after 5 or 10 attempts getting msg.[Maximum sessions of 1 for this principal exceeded]
I don't have this method, how to implement in a normal application.
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
in Console-
Session is destroyed --
Session is newly created ---
Session is destroyed ---
Session is newly created --
Session is destroyed --
Session is newly created--
Session is newly created ----
Info Anonymous User.....................

Categories

Resources