简体   繁体   中英

Simplest way to wrap custom authentication into Spring security?

Suppose I have simple bean, which can authenticate user by password and also know roles of any specified user:

interface MyBeanInterface {
    boolean check(String username, String password);
    List<String> roles(String username);
}

What is the simplest way to plug this functionality into Spring web application with basic HTTP security?

Simultaneously, I would like to annotate my controllers and service methods with @Secured annotation only. No any dot-separated predicates like here , please.

I can't break through that numerous "populators", "managers", "adapters" and other "configurers" in Spring Security API...

UPDATE

I wrote:

1) A Greeting class to return from controller

2) A GreetingController class to serve web requests /greeting1 and /greeting2 . I annotated first method with @Secured({"USER", "ADMIN"}) and the second with @Secured({"ADMIN"}) .

3) I wrote MyAuthService where I authenticated two user with different level of access.

4) I wrote AuthenticationProviderEx where implemented authenticate() method with calling MyAuthService bean.

5) I wrote SecurityConfig bean with configure() returning my provider.

The code is here in commit fdc2466 . In this state it does not asking authentication at all.

UPDATE 2

I have added @EnableGlobalMethodSecurity(securedEnabled = true) to SecurityConfig class and it started to ask username and password, but, unfortunately, returns error 403 on any request.

Make a custom authentication provider wrapper around your interface, something like:

@Component("customAuthenticationProvider")
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private MyBeanInterface myInterface;

    public Authentication authenticate(Authentication authentication) {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
        if (!myInterface.check(username, password)) {
            throw new BadCredentialsException("Bad username or password.");
        }
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String role : myInterface.roles(username)) {
            authorities.add(new SimpleGrantedAuthority(role));
        }
        return new UsernamePasswordAuthenticationToken(username, password, authorities);
    }

    public boolean supports(Class<?> clazz) {
        return UsernamePasswordAuthenticationToken.class.equals(clazz);
    }

}

And use it in your security config, with XML:

<authentication-manager>
  <authentication-provider ref="customAuthenticationProvider"/>
</authentication-manager>

Update: Also works with java config:

@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(customAuthenticationProvider);
    }

    /* rest of security config here */
}

The rest is pretty normal stuff.

Add a java config like this:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {  
    @Autowired YourAuthFilter youfilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterBefore(yourfilter, UsernamePasswordAuthenticationFilter.class);
    }
}

and YourAuthFilter would be like this:

@Component
public class YourAuthFilter extends GenericFilterBean {
    @Autowired private MyBeanInterface myBeanInterface;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // extract http basic tokens
        // use your myBeanInterface to authenticate
        // if it was successful, set Authentication 
        // by using SecurityContextHolder.getContext().setAuthentication(...)
        // otherwise, do whatever suits your application needs
        chain.doFilter(request, response);
    }
}

holmis83 approach is much better but if you want to use it in Java Config:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired CustomAuthenticationProvider customAuthenticationProvider;

    @Override
    @Autowired
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .authenticationProvider(customAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM