Reputation: 51259
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.
Upvotes: 3
Views: 1236
Reputation: 16644
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.
Upvotes: 2
Reputation: 48213
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();
}
}
Upvotes: 1