I have the following success handler that is part of the WebSecurityConfig.java file:
.successHandler(new AuthenticationSuccessHandler() {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
CustomOAuth2User oauthUser = (CustomOAuth2User) authentication.getPrincipal();
userAuthService.processOAuthPostLogin(oauthUser.getEmail());
response.sendRedirect("/list");
}
})
At the line where casting is supposed to take place, it gives the error:
java.lang.ClassCastException: class
org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser
cannot be cast to class
com.myapp.myapp.mvc.business.domain.user.CustomOAuth2User
How can I go about solving this?
The CustomOAuth2User class is as follows:
public class CustomOAuth2User implements OAuth2User {
private OAuth2User oauth2User;
public CustomOAuth2User(OAuth2User oauth2User) {
this.oauth2User = oauth2User;
}
#Override
public Map<String, Object> getAttributes() {
return oauth2User.getAttributes();
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return oauth2User.getAuthorities();
}
#Override
public String getName() {
return oauth2User.getAttribute("name");
}
public String getEmail() {
return oauth2User.<String>getAttribute("email");
}
}
I think your code are from codejava.net right? I also got the same issues and I fixed with update on the onAuthenticationSuccess function. I change the following code
CustomOAuth2User oauthUser = (CustomOAuth2User) authentication.getPrincipal();
userAuthService.processOAuthPostLogin(oauthUser.getEmail());
with this code
DefaultOidcUser oauthUser = (DefaultOidcUser) authentication.getPrincipal();
String email = oauthUser.getAttribute("email");
userDetailsService.processOAuthPostLogin(email);
And it's working now :)
You are getting this error because you have not defined the scopes explicitly in your app and by default scopes also include openId.
If you define your scopes in the application.properties like below you can cast the principal to CustomOAuth2User .
spring.security.oauth2.client.registration.google.scope=email,profile
If you wish to include openId in your scopes, then you need to cast your principal to DefaultOidcUser an you can create a CustomOidcUserService service like below
#Service
public class CustomOidcUserService extends OidcUserService {
#Autowired private UserRepository userRepository;
#Autowired private RoleRepository roleRepository;
#Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
final OidcUser oidcUser = super.loadUser(userRequest);
return oidcUser;
}
Related
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?
In configure() I have:
...
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService);
My CustomIodcUserService class:
#Service
public class CustomOidcUserService extends OidcUserService {
#Autowired
private UserRepository userRepository;
#Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser oidcUser = super.loadUser(userRequest);
Map attributes = oidcUser.getAttributes();
GoogleOAuth2UserInfo userInfo = new GoogleOAuth2UserInfo(attributes);
updateUser(userInfo);
return oidcUser;
}
private void updateUser(GoogleOAuth2UserInfo userInfo) {
User user = userRepository.findByEmail(userInfo.getEmail()).get();
if(user == null) {
user = new User();
}
user.setEmail(userInfo.getEmail());
user.setName(userInfo.getName());
System.out.println(userRepository.save(user));
}
}
But methods of this class are never called
Has anyone met this or how can I solve this or get UserInfo differently?
https://www.callicoder.com/spring-boot-security-oauth2-social-login-part-2/
This article describes how to do this, only there they used
userInfoEndpoint ()
.userService (customOAuth2UserService)
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();
}
}
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();
I store information about users in seperated tables owners,employees,users i am trying to use java configuration in spring security.
I have created three diferent authentication providers for each user type, but only the Users Provider is being triggered. I have read the spring security docs and the only way to do this seems to be is create class with multiple embedded classes extended from WebSecurityConfigurerAdapter but i don't want to do it this way because it requires a lot of duplicating code, is there any other way
I tryed to use the simple userDetailService inside which i send request to all tables in databese but still there is not results, only one query is buing executed and nothing, the only responce i get is:
2016-02-09 23:06:25.976 DEBUG 8780 --- [nio-8080-exec-1]
.s.a.DefaultAuthenticationEventPublisher : No event was found for the
exception
org.springframework.security.authentication.InternalAuthenticationServiceException
2016-02-09 23:06:25.976 DEBUG 8780 --- [nio-8080-exec-1]
o.s.s.w.a.www.BasicAuthenticationFilter : Authentication request for
failed:
org.springframework.security.authentication.InternalAuthenticationServiceException:
No entity found for query; nested exception is
javax.persistence.NoResultException: No entity found for query
But i never throw any exception!! And the most strange is that i can see in the debugger how the execution rapidly stops right after em.createQuery(..).getSingleResult().. and that's it, nothing more! There is no return statement no exception nothing, wtf!!
This is part of my current configuration:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(createAuthenticationProvider(employeeDetailService()))
.authenticationProvider(createAuthenticationProvider(ownerDetailsService()))
.authenticationProvider(createAuthenticationProvider(userDetailsService()));
}
#Bean
public OwnerDetailsService ownerDetailsService() {
return new OwnerDetailsService();
}
#Bean
public EmployeeDetailServiceImpl employeeDetailService() {
return new EmployeeDetailServiceImpl();
}
#Bean
public UserDetailsServiceImpl userDetailsService() {
return new UserDetailsServiceImpl();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(6);
}
#Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new MySimpleUrlAuthenticationSuccessHendler();
}
private AuthenticationProvider createAuthenticationProvider(UserDetailsService service) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(service);
provider.setPasswordEncoder(passwordEncoder());
provider.setHideUserNotFoundExceptions(true);
return provider;
}
User detail services:
#Service
public abstract class CustomUserDetailService implements UserDetailsService{
#Autowired
IDBBean dao;
protected CustomUserDetails getUser(GetUserByNameFunction function, String name) {
return createUser(function.get(name));
}
protected CustomUserDetails createUser(Authenticational user) {
return new CustomUserDetails(user, getAuthorities(user.getAuthority()));
}
protected List<GrantedAuthority> getAuthorities(String authority) {
return Collections.singletonList(new SimpleGrantedAuthority(authority));
}
}
Implementations
public class EmployeeDetailServiceImpl extends CustomUserDetailService {
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return super.getUser(dao::getEmployeeByEmail, email);
}
}
public class OwnerDetailsService extends CustomUserDetailService {
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return super.getUser(dao::getOwnerByEmail, email);
}
}
public class UserDetailsServiceImpl extends CustomUserDetailService {
#Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
return super.getUser(dao::getUserByEmail, userName);
}
}
Custom user details:
private Long id;
private String userEmail;
public CustomUserDetails(Authenticational user,
Collection<? extends GrantedAuthority> authorities) {
super(
user.getName(),
user.getPassword().toLowerCase(),
user.isEnabled(),
true,
true,
true,
authorities);
upadateValues(user);
}
private void upadateValues(Authenticational user) {
this.id = user.getId();
this.userEmail = user.getEmail();
}
Just to clarify something from the other answer:
Your authentication providers are stored in a list inside ProviderManager that iterates your authentication request through them. If your authentication provider throws AuthenticationException (BadCredentialsException extends AuthenticationException), then the ProviderManager will try another provider. If you set the hideUserNotFoundExceptions property, then it will also wrap and ignore UsernameNotFoundException and try another provider in this case too.
If I were you I would start by placing a debugging point inside ProviderManager's authenticate method. From there you can find out why the other authentication providers are not being called for their authenticate method.
Also I would think about having only one authentication provider with one UserDetailsService. It seems to me that you are doing a lot of complex not really needed operations like passing function to your abstract implementation when all you could do would be to have one UserDetailsService that would ask all your DAOs for a user. Which is basically what you're trying to accomplish but minus 2 authentication providers, minus 1 abstract class and minus 2 UserDetailsService implementations.
Spring Security will not try other authentication providers if a provider throws an AccountStatusException or if a UserDetailsService throws a UserNameNotFoundException or any other AuthenticationException
If you want other providers to be tried, then the loadUserByUserName methods of your UserDetailsServiceImpl and OwnerDetailsService should not throw the UserNameNotFound exception.
You should decide if you either want to return a dummy anonymous UserDetails object that will be used exclusively for fallback or some other mechanism to not throw the exception when a user is not available in your UserDetailsService implementation
this is an example I'v done to provide multi-auth for my application which have two diffirent user : admin and client .
ps: the admin and the client are two diffirent model.
public class CustomUserDetails implements UserDetails{
private Admin admin;
private Client client;
public CustomUserDetails(Admin admin) {
this.admin = admin;
}
public CustomUserDetails(Client client) {
this.client = client;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
#Override
public String getPassword() {
if((admin != null)&&(client==null)) {
return admin.getPassword();
}
else {
return client.getPassword();
}
}
#Override
public String getUsername() {
if((admin != null)&&(client==null)) {
return admin.getEmail();
}
else {
return client.getMail();
}
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
the CustomUserDetailsService class :
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
AdminRepository adminRepo;
#Autowired
ClientRepository clientRepo;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Admin admin = adminRepo.findByEmail(email);
Client client = clientRepo.findByEmail(email);
if((admin == null)&&(client==null)) {
throw new UsernameNotFoundException("User not found");
}
else if((admin != null)&&(client==null)) {
return new CustomUserDetails(admin);
}
else {
return new CustomUserDetails(client);
}
}
}