Why can't I go to admin REST API request mapping after I have logged in - Spring Boot - spring

I have some issues with my REST API, created from Spring Boot and Spring Security.
First I have created my Spring security configuration. As you see here, I have two paths with two different authorizations - USER and ADMIN.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private UserRepository userRepository;
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Set the default standard admin and password as admin, if not exist
User user = userRepository.findByUsername("admin");
if(user == null) {
user = new User();
user.setUserID(0);
user.setUsername("admin");
user.setPassword(passwordEncoder().encode("admin"));
Set<Role> roles = new HashSet<Role>();
Role role = new Role();
role.setRoleID(0);
role.setRolename("ADMIN");
roles.add(role);
user.setRoles(roles);
userRepository.save(user);
}
// Connect our database to spring and also with a password encoder
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/user/**").authenticated().anyRequest().hasAnyAuthority("ROLE_USER");
http.authorizeRequests().antMatchers("/admin/**").authenticated().anyRequest().hasAnyAuthority("ROLE_ADMIN");
http.httpBasic();
http.formLogin().permitAll();
}
}
And this is my controllers both user and admin.
#RestController
#RequestMapping("/admin")
public class AdminController {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#PostMapping("/addUser")
public String addUser(#RequestBody User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRepository.save(user);
return "Added user by admin";
}
#GetMapping("/adminHello")
public String adminHello() {
return "Admin say hello";
}
}
#RestController
#RequestMapping("/user")
public class UserController {
#GetMapping("/userHello")
public String userHello() {
return "processing..";
}
}
If I try to login to http://localhost:8080/login and write in my password and my username. Then I will be able to go in. Fine!
But these are the problems.
If I enter http://localhost:8080/user/userHello with a Admin account, I still get "processing..."
If I enter http://localhost:8080/admin/adminHello with a Admin account, I get
"Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Fri Sep 13 00:23:42 CEST 2019
There was an unexpected error (type=Forbidden, status=403).
Forbidden"
Why? Have I forgot something? My Accound have the ADMIN role in the database. Very clear.

Clearly your Role's are not working there is some issue with configuration.
Try this
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.formLogin().permitAll();
}

Related

Spring Custom Security With MySQL And JPA Giving 403 Access Denied

I am trying to access my rest api on postman by providing authentication using UserDetailsService, but each time I am firing the request every time request giving 403 Access Denied. The behavior is same for POST and GET method. I have read the other issues logged on forum but every answers says it is due to CSRF, I disabled it but issue remains same.
Complete code is on : https://github.com/afulz29/spring-security-demo.git
Please help me, I am struggling with this issue since 3 days.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer{
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/api/**").authenticated().anyRequest().hasAnyRole("ADMIN");
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("*");
}
}
#RestController
#RequestMapping("/api")
public class UserController {
#Autowired
private UserService userService;
#GetMapping(path = "/users")
public User getUserById(#RequestParam("userId") Integer userId) {
return userService.getUserById(userId);
}
#PostMapping(path = "/users", consumes = MediaType.APPLICATION_JSON_VALUE)
public User addUser(#RequestBody User user) {
return userService.addUser(user);
}
}
I see couple of problems with your security config:
BASIC AUTH is not enabled but you are trying to do Basic Auth in postman
Do the following to enable Basic Auth
http
.authorizeRequests()
...
.and()
.httpBasic();
I guess the POST /api/users is a user registration endpoint. You must whitelist this endpoint so that anyone can register
http
.authorizeRequests()
.antMatchers( HttpMethod.POST,"/api/users").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().hasAnyRole("ADMIN")
.and()
.httpBasic();
Test:
Create user
POST: localhost:8080/api/users
{
"userName" : "user1",
"password": "pass"
}
Get user info
GET: localhost:8080/api/users?userId=1 //use the correct ID
With Basic Auth: userName = user1, password = pass
BONUS Feedback:
User.userName --> you might want to make this field unique
#Repository this annotation is not required in your Repository interfaces
UserService interface. I don't see any reason to use the interface and impl.

Why SecurityContextHolder.getContext() == null?

I don't run my app, because auth == null. Why? I use spring boot 2.2.7.RELEASE and spring-security-oauth2-autoconfigure.
When starting SecurityContextHolder.getContext() always equal null
#Component
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
UserRepository userRepository;
#Autowired
PasswordEncoder passwordEncoder;
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/addPet").authenticated()
.antMatchers("/", "/**").access("permitAll")
.and().oauth2Login().loginPage("/login").defaultSuccessUrl("/", true).and().logout().logoutSuccessUrl("/")
.and().formLogin().loginPage("/login").loginProcessingUrl("/login").usernameParameter("login").passwordParameter("password")
.defaultSuccessUrl("/", true).and().logout().logoutSuccessUrl("/").deleteCookies("JSESSIONID").and().csrf().disable();
}
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(
"select login, password, enabled from users where login=?")
.authoritiesByUsernameQuery(
"select login, role from users where login=?");
}
}
I assume that you're referring to this line,
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
In this case, you don't need this line, it's not being used anywhere. In the bigger answer, SecurityContextHolder.getContext() will never be null, according to the documentation. The call to getAuthentication(), however, will be null unless there's an authenticated principal associated to the session.

spring security - Home page setup for authorize and unauthorize user

i'm stuck in spring security configuration. can any one help me for better solution. i have 2 jsp page one for login user and other for simple user. in which login user have option for logout and while in other jsp have option for login and signup.
my secuirty configuration class
#Configuration
#EnableWebSecurity
public class SecureConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Value("${winni.auth.exit}")
private String authExit;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().requestMatchers()
.antMatchers("/login","/web/**" ,"/exit","/action/**","/cart/**","/cart/xhr/**","/buyer/**")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll().and()
.logout().logoutSuccessUrl(authExit);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**","/account/**","/start/**");
}
}
home controller is
#RequestMapping("/")
public String sayHello(Principal principal) {
if (principal != null) {
return "login_user";
} else {
return "simple_user";
}
}
in every case Principal Object also null. how can solve this issue.

Getting redirected after login but I am not actually logged in Spring Security

Update: I tried implemting a CSRF token, and now when I try to log in, I always get redirected to the default Spring login page localhost:8080/login, and when I log in there again, it again redirects me to the login successful url....
This is my WebSecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
AccountDetailsService accountDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(accountDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.antMatchers("/api/me").permitAll()
.antMatchers( HttpMethod.POST,"/api/addthing", "/api/addotherthing").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.defaultSuccessUrl("http://192.168.1.105:3000/api/adminpage", true)
.and()
.logout()
.logoutSuccessUrl("http://192.168.1.105:3000/")
.logoutUrl("/logout")
.deleteCookies("JSESSIONID");
http.csrf().disable();
}
#Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
Here's my AccountDetailsService
#Service("userDetailsService")
#Transactional
public class AccountDetailsService implements UserDetailsService {
#Autowired
AccountService accountService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountService.findAccountByUsername(username);
return new org.springframework.security.core.userdetails.User(
account.getUsername(), account.getPassword(),
true, true, true, true,
accountService.getAuthorities(account)
);
}
}
Also the AccountSrvice:
#Service
public class AccountService {
#Autowired
AccountRepository accountRepository;
#Autowired
PasswordEncoder passwordEncoder;
public Account findAccountByUsername(String username){
return accountRepository.findFirstByUsername(username);
}
public List<GrantedAuthority> getAuthorities(Account account){
List<GrantedAuthority> roles = new ArrayList<>();
String role = account.getRole();
roles.add(new SimpleGrantedAuthority(role));
return roles;
}
public void createNewAccount(String username, String password, String role) {
Account account = new Account(username, passwordEncoder.encode(password), role);
accountRepository.save(account);
}
}
The problem that I am having is, after logging in, I get redirected to the /api/adminpage/ site, which means that the login is successful, right? But when I try getting my Principal with this method:
#CrossOrigin
#GetMapping("/api/me")
public Principal getMe(Principal principal){
return principal;
}
It just gives me an empty response, which means that I am not logged in.. Also when I try making a POST request to some of the urls in antMatches I can't... Can someone explain what am I doing wrong here?
Well, my whole day is now gone on this stupid little thing, but I got it working, and I'm not even happy about it anymore
The fix was to add "proxy": "http://192.168.1.105:8080" in my package.json in the React app.

How to renew access token with the refresh token in oauth2 in spring?

I am very new to spring and it is my first attempt at spring security with oauth2. I have implemented OAuth2 with spring security and I do get the access token and the refresh token. However, while sending the refresh token to get the new access token I got "o.s.s.o.provider.endpoint.TokenEndpoint - IllegalStateException, UserDetailsService is required."
The solution to similar problem by other users appeared to be attaching UserDetailsService with the endpoint.
So I did the same and now when I try to send the request to with grant_type: refresh_token and refresh_token: THE TOKEN along with the client id and secret, I get an error that the user was not found.
Please refer the WebSecurityConfiguration class below:
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Autowired
private UserDetailsService customUserDetailsService;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean ();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService)
.passwordEncoder(encoder());
}
#Override
protected void configure (HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/login**")
.permitAll()
.anyRequest()
.authenticated();
}
public PasswordEncoder encoder() {
return NoOpPasswordEncoder.getInstance();
}
}
Please refer the AuthorizationServerConfiguration class below:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.tokenStore(tokenStore());
.userDetailsService(userDetailsService);
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
}
Please refer the ResourceServerConfiguration class below:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter{
#Autowired
DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("scout").tokenStore(tokenStore());
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests (). antMatchers ("/oauth/token", "/oauth/authorize **").permitAll();
// .anyRequest (). authenticated ();
http.requestMatchers (). antMatchers ("/api/patients/**") // Deny access to "/ private"
.and (). authorizeRequests ()
.antMatchers ("/api/patients/**"). access ("hasRole ('PATIENT')")
.and (). requestMatchers (). antMatchers ("/api/doctors/**") // Deny access to "/ admin"
.and (). authorizeRequests ()
.antMatchers ("/api/doctors/**"). access ("hasRole ('DOCTOR')");
}
}
The CustomUserDetailsService class for reference if required:
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UsersRepository userRepository;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Optional<Users> usersOptional = userRepository.findByEmail(email);
Users user = null;
if(usersOptional.isPresent()) {
System.out.println(usersOptional.isPresent());
user = usersOptional.get();
}else {
throw new RuntimeException("Email is not registered!");
}
return new CustomUserDetails(user);
}
}
As I think, the server should only check for the validity of the refresh token as we don't pass the user details with refresh token. So I don't know why it requires the userDetails in the first place.
Please help and guide if I am missing something!
Thanks in advance.
I don't sure. But as I see your code in WebSecurityConfiguration could wired default InMemoryUserDetailsManager UserDetailsService .That could be reason why you have 2 different provider. In one you write, from the other you read users. Please try change your code as I show below and let me know if it help:
Was:
#Autowired
private UserDetailsService customUserDetailsService;
My vision how should be:
#Autowired
private CustomUserDetailsService customUserDetailsService;

Resources