How to perform RunAs using method security with Sp

2020-07-17 07:03发布

问题:

I have a web application with Spring MVC 3.2 and Spring Security 3.1

I'm using roles base security and have implemented UserDetailsService and UserDetails to provide GrantedAuthority.

I've enabled global method security with jsr250-annotations

Everything upto here is working as expected with signed in user method access restricted to the declared roles.

I have a further requirement to run certain methods called during application initialisation as a special user with a 'system role' ideally along the lines of JavaEE RunAs. I'm not sure how to do this in Spring Security.

Should I be trying to create a PreAuthenticatedAuthenticationToken with some made up values and a 'system role' authority.
I could then do something likeSecurityContextHolder.getContext().setAuthentication(token); when initialising the application.

Alternatively should I be trying to use the RunAsManager. It sounds like what I need but I have not found any simple examples of how I actually could use it.

I'm fairly new to Spring Security and I'm unsure of the best way to proceed.

回答1:

When my application starts

  • I run a post construct method in my spring bean to create a special user in memory with a system role.
  • This user object implements the
    org.springframework.security.core.userdetails.UserDetails
    interface.
  • I then use the user to create a security token
    org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
  • The token is then set in the Security Context.

    @Service
    @Transactional(readOnly = true)
    public class ApplicationConfiguration{
        @Inject
        MyService myService;
        @PostConstruct
        @Transactional(readOnly = false)
        public void init(){
    
            // ######## Application Starting #######"
    
            // Create a user that meets the contract of the Spring UserDetails interface
    
            UserAccountImpl sysAcc = new UserAccountImpl("system", "system", "system");
            UserRole role = new UserRole(Role.SYSTEM_ROLE);
            role.addUserPermission(Permission.SYSTEM);
            sysAcc.addUserRole(role);
            UserDetailsAdapter userDetails = new UserDetailsAdapter(sysAcc);
    
            // Create a token and set the security context
    
            PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( userDetails, userDetails.getPassword(), userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(token);
    
            // Now call service method with roles allowed  
    
            myService.initialiseSystem();
        }
    }
    

    ....

    public interface MyService {
        @RolesAllowed(SYSTEM)
        public void initialiseSystem();
    }
    


回答2:

Do you really need to attach a role to the said app initialization? Why not just extract the code that needs to be run during initialization like so:

public interface Service {

    @Secured("hasRole('USER')")
    void service();
}

public class DefaultService implements Service {

    @Override
    public void service() {
        doService();
    }

    public void doService() {
        // Implementation here
    }
}

...

public class AppInitializer {

    @Autowired
    private DefaultService service;

    public void init() {
        service.doService();
    }
}


回答3:

I believe that in this case a good solution for you would be to use the Spring Security OAuth because allow you have a greater integration to custom rules for access via tokens.

http://projects.spring.io/spring-security-oauth/