0

I am trying to learn basics of the spring security via spring boot, I created a project which includes postgresql setup also. Postgresql part is working as expected.

The problem is that after i am accessing the secure endpoint via correct credentials, I am just trying to access with the correct username and wrong password and expecting 401 error, but returns 200. And also returns to the content of the endpoint.

  • If I do a basic authentication request with username: dummy_user and password: 12345, response is 401 UnAuthorized
  • If I do a basic authentication request with username: dummy_user and password: 1234, response is 200
  • If I do a basic authentication request with username: dummy_user and password: 1234, response is 200
  • After response 200, If I do a basic authentication request with username: dummy_user and password: 12345, response is 200

Before running the project, I just added a dummy user:

INSERT INTO test_users (username,password) VALUES ('dummy_user','1234');

DTO is simple:

@Entity
@Table(name = "test_users")
public class UserDTO {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(name = "username")
    private String username;

    @Column(name = "password")
    private String password;
  • Configuration class:
@Configuration
public class ProjectBeanConfiguration {
    @Bean
    public UserDetailsService userDetailsService(){
       return new PostgresqlUserDetailsService();
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
}
  • And userDetailsService:
public class PostgresqlUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username){
        Optional<UserDTO> userDTOOptional = userRepository.findUserByUsername(username);
        UserDTO userInDb = userDTOOptional.orElseThrow(() -> new UsernameNotFoundException("Not Found in DB"));
        SecureUser secureUser = new SecureUser(userInDb);
        return secureUser;
    }
}
  • SecureUser is nothing but it just maps the UserDTO to UserDetails:
public class SecureUser  implements UserDetails {

    private final UserDTO userDTO;

    public SecureUser(UserDTO userDTO) {
        this.userDTO = userDTO;
    } 
    // ...
    @Override
    public String getPassword() {
        return userDTO.getPassword();
    }

    @Override
    public String getUsername() {
        return userDTO.getUsername();
    }
    // ...
  • There is only one controller:
@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello(){
        return "hello";
    }
}
  • Logs for: after response 200, If I do a basic authentication request with username: dummy_user and password: 12345, response is 200:
020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 1 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 2 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl@8e885cc7: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@8e885cc7: Principal: com...springsecurity.services.SecureUser@49860e95; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: com...springsecurity.services.SecureUser$$Lambda$890/0x0000000800848040@38f11ef2'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 4 of 15 in additional filter chain; firing Filter: 'CsrfFilter'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 5 of 15 in additional filter chain; firing Filter: 'LogoutFilter'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /hello' doesn't match 'POST /logout'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 6 of 15 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /hello' doesn't match 'POST /login'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 7 of 15 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 8 of 15 in additional filter chain; firing Filter: 'DefaultLogoutPageGeneratingFilter'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/hello'; against '/logout'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 9 of 15 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.a.www.BasicAuthenticationFilter  : Basic Authentication Authorization header found for user 'dummy_user'
2020-12-27 21:52:19.711 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 10 of 15 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.s.HttpSessionRequestCache        : saved request doesn't match
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 11 of 15 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 12 of 15 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter  : SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@8e885cc7: Principal: com...springsecurity.services.SecureUser@49860e95; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: com...springsecurity.services.SecureUser$$Lambda$890/0x0000000800848040@38f11ef2'
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 13 of 15 in additional filter chain; firing Filter: 'SessionManagementFilter'
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 14 of 15 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello at position 15 of 15 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /hello; Attributes: [authenticated]
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@8e885cc7: Principal: com...springsecurity.services.SecureUser@49860e95; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: com...springsecurity.services.SecureUser$$Lambda$890/0x0000000800848040@38f11ef2
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@5b81c050, returned: 1
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
2020-12-27 21:52:19.712 DEBUG 32988 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /hello reached end of additional filter chain; proceeding with original chain
2020-12-27 21:52:19.714 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@70616cef
2020-12-27 21:52:19.715 DEBUG 32988 --- [nio-8080-exec-2] o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
2020-12-27 21:52:19.716 DEBUG 32988 --- [nio-8080-exec-2] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
monstereo
  • 830
  • 11
  • 31
  • please post your security configuration, and your debug logs you can get the logs by following this post https://stackoverflow.com/a/47729991/1840146 – Toerktumlare Dec 27 '20 at 17:10
  • @Toerktumlare there is only one configuration class which is ProjectBeanConfiguration – monstereo Dec 27 '20 at 17:19
  • the logs you have posted can't be the debug logs for all your requests. its way too small and there are missing timestamps etc. there is only one single timestamp in the entire log – Toerktumlare Dec 27 '20 at 18:08
  • also, when storing passwords in a database you need to prefix the password with, what type of encoder that is being used. In your case its `{noop}1234` you can read about it here https://docs.spring.io/spring-security/site/docs/5.4.2/reference/html5/#authentication-password-storage-dpe-format – Toerktumlare Dec 27 '20 at 18:16
  • and where is the controller code for your endpoint? `/hello` – Toerktumlare Dec 27 '20 at 18:18
  • @Toerktumlare I updated the logs statement and also added timestamp, and there is only one endpoint in my project (I added the code section also). For {noop}, I am adding the user via sql not programmatic way – monstereo Dec 27 '20 at 18:55
  • i cant answer for the entire flow, since you have not posted all logs, but what it looks like since you are not logging out between your login attempts, it is assuming that you are already logged in. Probably because you are supplying the session cookie in your calls, which get prioritised. But i cant be sure since you have not provided the full logs so i know what is happening before. – Toerktumlare Dec 27 '20 at 20:30
  • As I can, you are not securing your endpoint. Either you should secure it my method security at method-controller or configure ant patterns security in `WebSecurityConfigurerAdaptor` class configure method. Then only your security filter will look for authentication, otherwise you patter "/hello" will be matched against "\**" a global pattern and validated. – Kumar Ashutosh Dec 27 '20 at 20:48
  • 1
    @Ashutosh per default Spring security will secure all endpoints. You can read more about the default configuration that is set up as soon as you place Spring security on the classpath https://docs.spring.io/spring-security/site/docs/current/reference/html5/#servlet-hello-auto-configuration one of the configuration is `Require an authenticated user for any interaction with the application` – Toerktumlare Dec 28 '20 at 00:21

2 Answers2

3

According to the logs:

SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@8e885cc7: Principal

This says that there is already a principal in the context when you are accessing hello.

And then logs tell us:

Previously Authenticated

So my conclusion (since this is not the full logs) and we have not seen how you do your requests is the following:

// No session established, you provide wrong credentials, you get a 401
 - If I do a basic authentication request with username: dummy_user and password: 12345, response is 401 UnAuthorized

// You authenticate correctly, we establish a session, you get a session cookie
- If I do a basic authentication request with username: dummy_user and password: 1234, response is 200

// You provide the session cookie in your request, we get a 200OK
- If I do a basic authentication request with username: dummy_user and password: 1234, response is 200

// You still provide the session cookie, we get a 200OK 
- After response 200, If I do a basic authentication request with username: dummy_user and password: 12345, response is 200

If you like to confirm that theory you should /logout between each login attempt or delete the cookie that gets set.

Toerktumlare
  • 12,548
  • 3
  • 35
  • 54
  • I think, session cookie was the reason, thanks mate. Interesting point is that I was using the Postman to send request, therefore postman somehow stores the session(s)!! – monstereo Dec 28 '20 at 07:15
  • 1
    postman always sends the cookies in each request, thats why unless you explicitly tell it not to. – Toerktumlare Dec 28 '20 at 07:53
0

The first time you login with the correct username and password Spring Security will create a session object and provide a session cookie for you. A session object is an object that stores information. When you logged in the first time it creates a authentication token that represents you have successfully logged and it gets stored in the session object. Every time you visit a secured endpoint on your web app you send the session cookie. The cookie identifies you as already being logged. To remove the session cookie go the logout end point url and this will remove your session.