UserDetailsService is never called(Spring boot security) - java

I have this SpringSecurity config
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl detailsService;
#Autowired
public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(detailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/index").access("hasRole('USER')");
http.userDetailsService(detailsService);
}
}
when I open index.html page I get 403 error. It is because user is anonymous. But I want check user and set role and detail before opened this page. For this i write this service
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private HttpServletRequest request;
#Autowired
AuthService authService;
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
String ipAddress = request.getRemoteAddr();
AuthLkUser authLkUserByIp = authService.getAuthLkUserByIp(ipAddress);
if (authLkUserByIp == null) return null;
boolean b = authService.checkAuthLkUser(authLkUserByIp);
if (b) return null;
Set<GrantedAuthority> roles = new HashSet();
roles.add(new SimpleGrantedAuthority("USER"));
UserDetails userDetails = new org.springframework.security.core.userdetails.User(authLkUserByIp.getMsisdn(),
authLkUserByIp.getSubsId(), roles);
return userDetails;
}
}
But this service is never called.

In Spring Boot if you have a custom Spring Security Configuration then it will not use Auto Configuration for Spring Security.
In your Spring security configuration you haven't given anyway to authenticate a user. There are form based, basic header based, etc authentication available in Spring Security. You should use one of that to authenticate user so that during authentication your UserDetailsServiceImpl will be used to load the user.
Follow this post for form based login

Related

403 forbidden in Spring Security with Custom UserDetailsService only [duplicate]

This question already has answers here:
Springboot Security hasRole not working
(3 answers)
Closed 1 year ago.
I have following files in spring boot project for Spring Security. When I use inMemoryAuthentication it works but when I go with custom UserDetailsService it doesn't work. Custom UserDetailsService class gets called but still it gives 403 (when I am trying to access /user) but it works for open urls (/usr).
import org.springframework.security.core.userdetails.User
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//not using parameter which is being passed as trying to figure out the problem first.
UserDetails user = User.withUsername("abc").password("abc").authorities("ADMIN").build();
return user;
}
}
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsServiceImpl userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
// auth.inMemoryAuthentication().withUser("abc").password("abc").roles("ADMIN");
}
#Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/user").hasRole("ADMIN")
.antMatchers("/").permitAll()
.and().formLogin();
}
}
#RestController
#RequestMapping("/api")
public class UserController {
#GetMapping("/usr")
public ResponseEntity<String> getOpenResponse() {
return ResponseEntity.ok("You are accessing open url");
}
#GetMapping("/user")
public ResponseEntity<String> getSecuredResponse() {
return ResponseEntity.ok("You are accessing secured path");
}
}
What am I doing wrong here? Am I missing something?
The problem is here :
UserDetails user = User.withUsername("abc").password("abc").authorities("ADMIN").build();
You set your user’s authority to "ADMIN", but in SecurityConfig class you expect the user to have a role "ADMIN", which is, in fact, a shortcut for an authority of "ROLE_ADMIN" :
http.authorizeRequests()
.antMatchers("/api/user").hasRole("ADMIN")
To solve the problem you should define the user’s role :
User.withUsername("abc").password("abc").roles("ADMIN")

How to add a new User with Spring Security without restart spring boot

After finishing startup spring boot, Spring Security's configuration can not update . how to add new user or update user password or user role without restart spring boot? Because the page was redirected to the /login?error page, when I use new password to login.
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
MemberMapper memberMapper;
Logger logger = Logger.getLogger(this.getClass());
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.addFilterBefore(new loginFilter(), AnonymousAuthenticationFilter.class).
authorizeRequests().antMatchers("/","/register/**","/log/**").permitAll();
http.formLogin().loginPage("/log/toLogin")
.loginProcessingUrl("/log/login")
.usernameParameter("memacc")
.passwordParameter("mempwd")
.failureHandler(new AppsAuthenticationFailureHandler());;
http.logout().logoutSuccessUrl("/");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
List<Members> allMembers = memberMapper.getAllMembers();
for (Members members : allMembers){
String[] roleList = members.getRoleList().split(",");
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser(members.getMemacc()).password(new BCryptPasswordEncoder().encode(members.getMempwd())).roles(roleList);
}
}
}
When you do the login process, spring boots saves the user profile in memory.
If you add a new ROLE for a logged user for example and save in database, the user profile in memory didn't change and thats why you have to update this data.
You can make it with:
User user = userService.findById(idUser);
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getRoles()));
With this command, you update the user profile in spring-security memory.
thanks all,
I found the solution.
...
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
MyUserDetailsService myUserDetailsService;
Logger logger = Logger.getLogger(this.getClass());
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.addFilterBefore(new loginFilter(), AnonymousAuthenticationFilter.class).
authorizeRequests().antMatchers("/","/register/**","/log/**").permitAll();
http.formLogin().loginPage("/log/toLogin")
.loginProcessingUrl("/log/login")
.usernameParameter("memacc")
.passwordParameter("mempwd");
http.logout().logoutSuccessUrl("/");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
}
...
use the class implement UserDetailsService inteface. when user log in then authentate that user.
...
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
MemberMapper memberMapper;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Members member = memberMapper.getOneMemberByMemacc(username);
if(member == null)
throw new UsernameNotFoundException(username + " not found");
UserDetails userDetails = User.builder()
.username(member.getMemacc())
.password("{noop}" + member.getMempwd())
.roles(member.getRoleList()).build();
return userDetails;
}
}
...

Spring Security to support multiple authentication provider LDAP authentication and JPA

I'm trying to implement an authentication and authorization service for an ongoing spring-boot project. I have implemented a JPA based authentication provider and it is working fine. How do I add LDAP authentication provider to the same project and switch between the authentication methods depending on the user authentication type?
Below is my code
#Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UsernamePasswordAuthProvider authProvider;
#Autowired
private LdapAuth ldapAuth;
#Autowired
private LdapAuthenticationpopulator ldapAuthenticationpopulator;
private String ldapUrls = "ldap://localhost:3890";
private String ldapSecurityPrincipal = "cn=admin,dc=mycompany,dc=com";
private String ldapPrincipalPassword = "admin";
private String userDnPattern = "uid={0}";
#Autowired
private UsernamePasswordAuthFilter usernamePasswordAuthFilter;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Order(1)
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication()
.contextSource()
.url(ldapUrls)
.managerDn(ldapSecurityPrincipal)
.managerPassword(ldapPrincipalPassword)
.and()
.userDnPatterns(userDnPattern)
.ldapAuthoritiesPopulator(ldapAuthenticationpopulator);
}
#Override
#Order(2)
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authProvider);
}
#Override
protected void configure(HttpSecurity http) {
http.addFilterAt(usernamePasswordAuthFilter,
BasicAuthenticationFilter.class);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Though my LDAP credentials are correct it is not reaching that method.
How do I get the authentication method from DB ex(LDAP, JPA, SSO) for my application and execute the corresponding auth provider method?
I have gone through multiple documents for MultipleAuthenticationProviders but I couldn't find much clarity
Please let me know if there is any possible solution for this.
Thanks in advance
I found one solution to this. I have put up LDAP enabled or JPA auth enabled properties in DB and loading them at run time depending upon the boolean value I'm calling the particular auth method.

Why is my oauth2 config not using my custom UserService?

I'm trying to use authentication by google. I am using springboot2, so most of the configuration is automatic. The authentication itself works good, but afterwards I would like to populate Principal with my own data (roles, username, and stuff).
I've created MyUserService that exteds DefaultOauth2UserService, and I am trying to use it as follows:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
MyUserService myUserService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.userService(myUserService);
}
}
I've checked with debuger, that application never actually uses loadUser methods. And here is implementation of MyUserService:
#Component
public class MyUserService extends DefaultOAuth2UserService {
#Autowired
UserRepository userRepository;
public MyUserService(){
LoggerFactory.getLogger(MyUserService.class).info("initializing user service");
}
#Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
Map<String, Object> attributes = oAuth2User.getAttributes();
String emailFromGoogle = (String) attributes.get("email");
User user = userRepository.findByEmail(emailFromGoogle);
attributes.put("given_name", user.getFirstName());
attributes.put("family_name", user.getLastName());
Set<GrantedAuthority> authoritySet = new HashSet<>(oAuth2User.getAuthorities());
return new DefaultOAuth2User(authoritySet, attributes, "sub");
}
}
Actually the solution was just to add another property for google authentication:
spring.security.oauth2.client.registration.google.scope=profile email
Not sure, what is the default scope, and why entrance to the service is dependent on scope, but without this line the code never reached my custom service.
I think you're missing the #EnableOAuth2Client annotation at the top of your SecurityConfig class.
Regardless, I made an examplewith a Custom user service for oauth2 here https://github.com/TwinProduction/spring-security-oauth2-client-example/ if it helps

How to configure multiple HttpSecurity with UserDetailsService using spring boot security?

I'm working on with spring boot security layer to authenticate and authorize the user.Now, i would like to do some sample app using multi http security configuration.I have the scenario like there will be two login pages with different URL mappings("/managementLogin","/othersLogin").
I can understood how to configure multi httpsecurity configs but i need to validate the users from two tables.If the management users loggedIn i need to validate the user from management table through DAO layer using UserDetailsService else if any other users loggedIn i need to validate from other_users table.
Could anybody help me to know how to configure the multi http config and dao layer using UserDetailsService with spring boot security ?
Here is my basic code snippet,
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
UserDetailsService userDetailsService;
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// for testing authentication purpose using inMemory db
/*
* auth.inMemoryAuthentication().withUser("user").password("user").roles
* ("USER").and().withUser("admin") .password("admin").roles("ADMIN");
*/
// Dao based authentication
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/home").permitAll();
http.authorizeRequests().antMatchers("/rest/**").authenticated();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.formLogin().successHandler(authenticationSuccessHandler);
http.formLogin().failureHandler(authenticationFailureHandler);
http.logout().logoutSuccessUrl("/");
// CSRF tokens handling
http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/registerUser","/register.html");
}
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
}
TIA..,
Implement a custom UserDetailsService like this:
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserDaoTableOne userDaoTableOne;
#Autowired
private UserDaoTableTwo userDaoTableTwo;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = userDaoTableOne.find(username);
if(user == null){
user = userDaoTableTwo.find(username);
}
if (user == null) {
throw new UsernameNotFoundException(String.format("Username '%s' not found", username));
}
return user;
}
}
Implement two DaoAuthenticationProvider with his own UserDetailsService and inject both providers to the authenticationManager.
I don't know what is the requisite for two distinct login endpoints but at first I think is a bad idea.
You can create different Authentication objects an let the AuthenticationManager choose the correct AuthenticationProvider based in the supports method.
Indeed you will need to use, two user detail services. But, that wont be enough. I suggest you to create another ApplicationSecurity2 class with different order.
Spring security is built on an ordered list of filter chains.
see the answer given here by Dave Sayer. Then you can handle different urls, as you want.
in my case I checked into two repositories, Below an exemple that I use:
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AbstractUser user;
try {
user = clientRepository.findByUsername(username);
}
catch (Exception userException) {
try {
user = adminRepository.findByUsername(username);
}
catch (Exception adminException) {
throw new UsernameNotFoundException("No user present with username : " + username);
}
}
return user;
}
I have to handle around same issue , i have autowired httprequest class in userdetail service and get request params type and drive my logic based on that.
you can directly solve the issue as the recommended solutions, but you can create a simple trick to define two different UserDetailsService as here I have two user one as a normal user and another as an editor :
editor
#Log4j2
#RequiredArgsConstructor
#Service
public class EditorService implements UserDetailsService {
private final EditorRepository editorRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if(username == null || "".equals(username)){
throw new UsernameNotFoundException("null value");
}
Optional<Editor> editor = editorRepository.findByUsername(username);
if(editor.isPresent()){
log.info("created under editor service: " + editor.get());
return editor.get();
}
throw new UsernameNotFoundException("does not exists");
}
}
user
#Log4j2
#RequiredArgsConstructor
#Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if(username == null || "".equals(username)){
throw new UsernameNotFoundException("null");
}
Optional<User> user = userRepository.findByUsername(username);
if(user.isPresent()){
log.info("cretaed under User service : " + user.get());
return user.get();
}
throw new UsernameNotFoundException("does not exists");
}
}
then on the configurations side, we can use of spring order mechanism :
user config :
#EnableWebSecurity
#Configuration
#RequiredArgsConstructor
#Order(1)
public class UserWebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserService userService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/user/**")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder(10);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.userService).passwordEncoder(passwordEncoder());
}
}
Editor config :
#EnableWebSecurity
#Configuration
#RequiredArgsConstructor
public class EditorWebSecurityConfig extends WebSecurityConfigurerAdapter {
private final EditorService editorService;
#Lazy
private final PasswordEncoder passwordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http // all other requests handled here
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.editorService).passwordEncoder(passwordEncoder);
}
}

Categories

Resources