0

I'm trying to manually set the authentication in order to implement a system of user permissions. My ultimate goal is to be able to use the @PreAuthorize() annotation to restrict certain methods. Below is my security configuration.

@Order(1)
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    @Conditional(OpenSecurityCondition.class)
    public class OpenSecurityConfig extends WebSecurityConfigurerAdapter {

        private final PasswordEncoder passwordEncoder;

        public OpenSecurityConfig(PasswordEncoder passwordEncoder) {
            this.passwordEncoder = passwordEncoder;
        }
        
        @Bean("authenticationManager")
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
              http
                      .csrf()
                      .disable()
                      .authorizeRequests()
                      .anyRequest()
                      .permitAll();
        }
    }

Here is where I manually set the authentication object:

 UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                username, null, privileges);
        SecurityContextHolder.getContext().setAuthentication(authToken);
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        System.out.println("Principal: " + auth.getPrincipal());
        System.out.println("Name: " + auth.getName());
        System.out.println("Authorities: " + auth.getAuthorities());
        System.out.println("Credentials: " + auth.getCredentials());
        System.out.println("Class: " + auth.getClass());

I'm printing some of the attributes of the authentication object just to prove to myself that they are set correctly. This is the output of the above print statements:

Principal: John Doe
Name: John Doe
Authorities: [Calendars-R, Carrier-R, Config-R, Default-R, Holiday-R, Location-R, ShipMethodCustom-R, ShipMethodOverride-R, Shipment-R, Simulator-R, Zones-R]
Credentials: null
Class: class org.springframework.security.authentication.UsernamePasswordAuthenticationToken

Everything looks good so far. The principal, and more importantly, the proper authorities are there. This will allow me to use the @PreAuthorize annotation to check for those authorities. However, in a different controller, I'm noticing that the authentication object has lost all of the information that I just set. In this other controller, if I call the following:

System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal());

It prints

anonymousUser

...and all of the other authorities and information that I had previously set are lost. This prevents me from being able to use the @PreAuthorize annotation to secure my application. My question is, how can I get the Authentication object to persist across different requests? Why would this not be its default behavior?

For your convenience, I'll go ahead and throw some more code in here. Here is the primary controller handling the login:

@RestController
@RequiredArgsConstructor
@RequestMapping("/user")
public class UserController {

    private final UserService userService;
    private final RoleService roleService;
    private final UserDetailsService userDetailsService;
    private final AuthenticationManager authenticationManager;

    @PostMapping
    public void receiveUsername(@RequestBody Map<String, Object> json_data, HttpServletRequest request, HttpServletResponse response) {
        String username = json_data.get("username").toString();

        User foundUser = userService.findByUsername(username);

        if(foundUser == null) {
            System.out.println("User not found. Creating new user.");
            foundUser = userService.create(new User(username));
            roleService.assignRole(foundUser.getId(), roleService.USER);
        }
        
        List<Role> userRoles = (List<Role>) foundUser.getRoles();

        UserDetails details = userDetailsService.loadUserByUsername(username);
        List<GrantedAuthority> privileges = details.getAuthorities().stream().collect(Collectors.toList());
        System.out.println(privileges);

        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, "password", privileges);
        Authentication auth = authenticationManager.authenticate(authToken);
        SecurityContext sc = SecurityContextHolder.getContext();
        sc.setAuthentication(auth);
        HttpSession session = request.getSession(true);
        session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, sc);
    }
}
eswaters
  • 1
  • 1
  • without posting any of the controllers, or a small reproducible project this is impossible to answer with you just posting a System.out.println where you claim you get what you get. Voted to close – Toerktumlare Jun 22 '22 at 19:52
  • @Toerktumlare I just posted the only relevant controller. There is simply no way that I can give you a reproducible project... I have hundreds of files going on here. The question is pretty general, and hardly project-specific. I'm manually creating an authentication object and assigning it to the SecurityContext. This SecurityContext is wiped clean between requests, defeating the purpose of ever authenticating in the first place. I'm just trying to get it to persist between different requests. – eswaters Jun 22 '22 at 21:16
  • any reason to why you are not using the default `formLogin` that comes with spring security and instead doing all this custom security which i might remind you is bad practice, all custom security is bad practice. – Toerktumlare Jun 22 '22 at 21:26
  • @Toerktumlare I wish I could, but this is a company project that I just joined on and our authentication just takes the username and password on the front end and sends it directly to our separate authentication server... it never touches the API. My job is to set up user permissions, so I'm just trying to simulate the user being authenticated through spring security with their permissions being pulled from a database so that I can use the "@PreAuthorize" annotation to control who can do what. – eswaters Jun 22 '22 at 21:39
  • `just takes the username and password on the front end and sends it directly to our separate authentication server... it never touches the API` this sentence makes no sense, as the code you have provided takes a username and password and authenticates the user and sets up the security context. So clearly there is some form of authentication done here. And all of this is bad practice – Toerktumlare Jun 22 '22 at 21:42
  • @Toerktumlare I am noticing a new issue that may be related... I'm trying to store the SecurityContext inside the session as taught by https://www.baeldung.com/manually-set-user-authentication-spring-security, but my session is getting reset with a new session id every time that I perform a new request. Any idea why this might happen? – eswaters Jun 22 '22 at 21:42
  • @Toerktumlare I know, I'm sorry, its a bit confusing. Since the authentication is happening in a separate place that I have no control over, I'm just passing the username to this API so that I can assign roles and permissions to different usernames. I have no interest in authentication here. I'm only trying to achieve authorization. But Spring Security seems to want to force me to be authenticated. – eswaters Jun 22 '22 at 21:45
  • without you producing a reproducible example and i cant see the bigger picture then no. And your explanation is tbh lacking. Also dont read baeldung, it has a lot of misinformation. The only source you should read is the official spring security documentation. good luck – Toerktumlare Jun 22 '22 at 21:46

0 Answers0