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")
Related
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;
}
}
...
I have created a method for persisting user details in the database and i also have a controller which is exposed at the endpoint /register. I wanted to make the /register endpoint available to all. I have used spring security and gave permit all for the /register end point.
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserDetailsServiceImpl userDetailsService;
#Autowired
public WebSecurityConfiguration(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests(
request -> request.antMatchers(HttpMethod.POST,"/register").permitAll()
.anyRequest().authenticated()
);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Can someone please explain or help me out why permitAll is not working in my case. As per the code i have written the /register end point should return the user details but it returns 403. The /register endpoint is a rest endpoint which takes the user details as input and return the user details as output once the detal is persisted to the database.
#Slf4j
#RestController
public class RegistrationController {
private final UserDetailsServiceImpl userDetailsService;
#Autowired
public RegistrationController(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
#PostMapping(value = "/register")
public ResponseEntity<Users> registerNewUser(#Valid #RequestBody Users users) throws EmailAlreadyExistsException {
Users usersDetails = userDetailsService.processRegistration(users);
log.info("{}, Information: Successfully persisted new user",this.getClass().getSimpleName());
return new ResponseEntity<>(usersDetails,HttpStatus.OK);
}
}
I guess you are calling the url via curl or postman. You must then disable CSRF or use a GET mapping instead.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests(
request -> request.antMatchers(HttpMethod.POST,"/register").permitAll()
.anyRequest().authenticated()
);
}
I'm new to spring security so for learning purpouses I created a api using spring-boot 2.0.3.RELEASE and spring-boot-starter-security.
Here is what I got
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("admin")).roles("USER");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Controller class
#RestController
#RequestMapping(value = "/security")
public class AppController {
#Autowired
SpringSecurityService service;
#GetMapping(value = "/{id}")
public Person findOne(#PathVariable("id") final int id) {
return service.findOne(id);
}
}
When I try to consume the get method findOne via some browser a Login form is promped since all browsers support basic auth I try to login using admin as user and password but an error from wrong credentials is shown.
I tried also to consume the mehod via postman sending Authorization and $2a$10$c6MFPW.7MD7a.2V2rJYlXO0.YOLQEmsbu5GBmFsf.jShduBPenQ6O as the value.
I got the value for the password from here:
System.out.println(new BCryptPasswordEncoder().encode("admin"));
I know this is some rookie mistake but I dont know what I'm missing
Tried your code out in my own project. It would appear you're just missing an #Configuration annotation on your WebSecurityConfigurerAdapter thus the default configuration is taking place. Where the user name, and the logged password in the console would work.
Working example - https://github.com/DarrenForsythe/sof-60178305
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
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);
}
}