I am trying to get familiar with spring boot security, my API is a simple GET/POST to 127.0.0.1:8080/employee/
with a simple autoconfiguration like this.
#Configuration
public class SecurityConfig implements WebMvcConfigurer {
#Configuration
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter{
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password("{noop}user1").authorities("USER").and()
.withUser("user2").password("{noop}user2").authorities("USER").and()
.withUser("admin").password("{noop}admin").authorities("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/employee/**").authorizeRequests().anyRequest().hasRole("ADMIN");
}
}
}
This always gives me 403 Forbidden.
Have tried this:-
antMatcher("/employee*"), works but for any user. Can I get some help in understanding how this pattern works, I just need to restrict "/employee" or "/employee/" or "/employee/1" to Admin.
Your current configuration will only restrict any paths under employee e.g. employee/1 but not /employee. Additionally you are not doing anything with the employee matcher as you go back to authorizeRequests, then configure anyRequest has a role ADMIN
To restrict employee and any path underneath it to ADMIN.
http.authorizeRequests().antMatchers("/employee**", "/employee/**").hasRole("ADMIN").httpBasic();
Using ** will capture directories in path.
See
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html
and
https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/util/matcher/AntPathRequestMatcher.html
Using a pattern value of /** or ** is treated as a universal match, which will match any request. Patterns which end with /** (and have no other wildcards) are optimized by using a substring match — a pattern of /aaa/** will match /aaa, /aaa/ and any sub-directories, such as /aaa/bbb/ccc.
I would also recommend testing your security configuration via the #WebMvcTest slice test
https://www.baeldung.com/spring-security-integration-tests
From the above a simple example would be,
#RunWith(SpringRunner.class)
#WebMvcTest(SecuredController.class)
public class SecuredControllerWebMvcIntegrationTest {
#Autowired
private MockMvc mvc;
// ... other methods
#WithMockUser(roles= {"admin"})
#Test
public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
mvc.perform(get("/employee/1").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
//Repeated for other Paths
}
Related
I want to just secure the method but not the end point( API) with #Secured. In the over ride method that extends GlobalMethodSecurityConfiguration i will have way to call the some dependent service if the user is allowed depending the on input passed in #Secured Annotation.
for example
#Secured("abc")
public void secureMethod(){
system.out.println("You are allowed)
}
The interceptor (that extends GlobalMethodSecurityConfiguration) method will check if the input to #Secured method has input as abc or something and then return access_denied to this method only. The input to method will be passed by calling service and can be injected at run time.
How i can do that w/o having login screen.
I'm not sure I fully got your question, but to disable the form-login including login/logout screens you would do this:
#Configuration
#EnableWebSecurity
public static class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity security) throws Exception
{
security.formLogin().disable();
}
}
Note: As you are using GlobalMethodSecurityConfiguration , you can try and nest this class INSIDE it.
So:
#Configuration
#EnableGlobalMethodSecurity
public class SecurityConfig extends GlobalMethodSecurityConfiguration {
// do whatever is needed here
#Configuration
public static class WebSecurityConfig extends
WebSecurityConfigurerAdapter { // nesting
#Override
protected void configure(HttpSecurity security) throws Exception
{
security.formLogin().disable();
}
}
}
I have this little OAuth server class and I am using Spring Boot 2.0.4 and the spring-security-oauth2-autoconfigure 2.0.0.RELEASE dependency :
#RestController
#SpringBootApplication
#EnableAuthorizationServer
#Order(200) // really needed ?
public class MyOAuthServerApplication extends WebSecurityConfigurerAdapter {
#RequestMapping({ "/me" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
// #formatter:on
}
}
#Bean
#Override
public UserDetailsService userDetailsService() {
UserDetails mary =
User.withUsername("mary")
.password("{bcrypt}$2a$10$B3NUb0x.MYnSfx7WJItrvO/ymEQwLCKQNehmCuA8keL1uTyHizI0i")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(mary);
}
public static void main(String[] args) {
SpringApplication.run(MyOAuthServerApplication.class, args);
}
}
This seems to work well with and without the #Order(200) annotation.
So is this annotation really needed ?
The Order annotation is used to define the injection precedence.
Read more her: https://www.baeldung.com/spring-order
In your case it's because of the EnableResourceServer annotation. And you must keep the annotation.
From the doc:
The #EnableResourceServer annotation creates a security filter with
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER-1) by default, so by
moving the main application security to
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) we ensure that the
rule for "/me" takes precedence.
Please find the tutorial here: https://spring.io/guides/tutorials/spring-boot-oauth2/
You need it if you have another WebSecurityConfigurerAdapter configuration.
For example if you allow users to login via login form with a different UserDetailsService and so on. Then this should be tried before your oauth authentification and thus needs a lower order, for example #Order(199).
Another example would be different configuration for your API access.
If you don't have any other configuration, then you don't need to set the order.
Setting the order to 200 also seems to be an arbitrary value, that should simply be higher then the others and thus executed last.
How do I test a REST Controller that uses Oauth2 (client)? I need to mock the oauth2 and I am stuck. Any help would be appreciated.
Hope that this answer may help.
Actually, when using OAuth2 with a ResourceServerConfiguration, you will have a stateless security, which will throw away any effort in mocking users beforehand.
What you should do to mock users is:
Create a TestOnly loaded ResourceServerConfiguration which overrides your standard one in this way:
public class TestConfiguration extends ResourceServerConfiguration {
#Override
public void configure(ResourceServerSecurityConfigurer res) {
res.stateless(false);
super.configure(resources);
}
}
add the #WithMockUser to the tests:
#Test
#WithMockUser(username ="username_admin", authorities = {"I_AM_LEGEND"})
public void myTestWithUser() throws Exception {
this.mvc.perform(get("/getUsername")).andExpect(content().text().contains("username_admin"));
}
#Test
public void myTestWithNoUser() throws Exception {
this.mvc.perform(get("/getUsername")).andExpect(status().isUnauthorized());
}
NOTE: I wrote the code from memory
I am using spring-boot and I have declared a filter like so:
public static class ZeroLeggedOAuthProviderProcessingFilter extends ProtectedResourceProcessingFilter {
ZeroLeggedOAuthProviderProcessingFilter(LTIConsumerDetailsService ltiConsumerDetailsService, LTIOAuthNonceServices ltioAuthNonceServices, OAuthProcessingFilterEntryPoint oAuthProcessingFilterEntryPoint, OAuthAuthenticationHandler oAuthAuthenticationHandler, OAuthProviderTokenServices oauthProviderTokenServices) {
super();
log.info("CONSTRUCT Zero Legged OAuth provider");
setAuthenticationEntryPoint(oAuthProcessingFilterEntryPoint);
setAuthHandler(oAuthAuthenticationHandler);
setConsumerDetailsService(ltiConsumerDetailsService);
setNonceServices(ltioAuthNonceServices);
setTokenServices(oauthProviderTokenServices);
//setIgnoreMissingCredentials(false); // die if OAuth params are not included
}
}
#Bean(name = "zeroLeggedOAuthProviderProcessingFilter")
public ZeroLeggedOAuthProviderProcessingFilter oauthProviderProcessingFilter() {
return new ZeroLeggedOAuthProviderProcessingFilter(oauthConsumerDetailsService, oauthNonceServices, oauthAuthenticationEntryPoint(), oauthAuthenticationHandler, oauthProviderTokenServices());
}
I apply that filter to a single path like so (which seems to work):
#Configuration
#Order(1) // HIGHEST
public static class OAuthSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
ZeroLeggedOAuthProviderProcessingFilter oauthProviderProcessingFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
// filters must be ordered: see http://docs.spring.io/spring-security/site/docs/3.2.0.RELEASE/apidocs/org/springframework/security/config/annotation/web/HttpSecurityBuilder.html#addFilter%28javax.servlet.Filter%29
http.antMatcher("/oauth/**")
.addFilterBefore(oauthProviderProcessingFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().anyRequest().hasRole("OAUTH");
}
}
However, it ends up being applied to all paths and I think it is because of some automatic registration that is happening (based on this in the logs).
o.s.b.c.embedded.FilterRegistrationBean : Mapping filter:
'zeroLeggedOAuthProviderProcessingFilter' to: [/*]
Is there a way to stop my filter from being automatically registered?
I ended up solving it this way instead (since the FilterRegistrationBean caused other problems).
My filter definition:
public static class ZeroLeggedOAuthProviderProcessingFilter extends ProtectedResourceProcessingFilter {
ZeroLeggedOAuthProviderProcessingFilter(LTIConsumerDetailsService ltiConsumerDetailsService, LTIOAuthNonceServices ltioAuthNonceServices, OAuthProcessingFilterEntryPoint oAuthProcessingFilterEntryPoint, OAuthAuthenticationHandler oAuthAuthenticationHandler, OAuthProviderTokenServices oauthProviderTokenServices) {
super();
log.info("CONSTRUCT Zero Legged OAuth provider");
setAuthenticationEntryPoint(oAuthProcessingFilterEntryPoint);
setAuthHandler(oAuthAuthenticationHandler);
setConsumerDetailsService(ltiConsumerDetailsService);
setNonceServices(ltioAuthNonceServices);
setTokenServices(oauthProviderTokenServices);
//setIgnoreMissingCredentials(false); // die if OAuth params are not included
}
}
And my http config which applies that filter to the oauth path only:
#Configuration
#Order(1) // HIGHEST
public static class OAuthSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
private ZeroLeggedOAuthProviderProcessingFilter zeroLeggedOAuthProviderProcessingFilter;
#Autowired
LTIConsumerDetailsService oauthConsumerDetailsService;
#Autowired
LTIOAuthNonceServices oauthNonceServices;
#Autowired
OAuthAuthenticationHandler oauthAuthenticationHandler;
#Autowired
OAuthProcessingFilterEntryPoint oauthProcessingFilterEntryPoint;
#Autowired
OAuthProviderTokenServices oauthProviderTokenServices;
#PostConstruct
public void init() {
zeroLeggedOAuthProviderProcessingFilter = new ZeroLeggedOAuthProviderProcessingFilter(oauthConsumerDetailsService, oauthNonceServices, oauthProcessingFilterEntryPoint, oauthAuthenticationHandler, oauthProviderTokenServices);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// added filters must be ordered: see http://docs.spring.io/spring-security/site/docs/3.2.0.RELEASE/apidocs/org/springframework/security/config/annotation/web/HttpSecurityBuilder.html#addFilter%28javax.servlet.Filter%29
http.antMatcher("/oauth/**")
.addFilterBefore(zeroLeggedOAuthProviderProcessingFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().anyRequest().hasRole("OAUTH");
}
}
This also solved another issue I was having where my filter was appearing before the security filters (which caused other issues) so I think this is the best solution. I wish there was some mechanism to tell spring to ignore a filter in the global filters listing as well but this works in the meantime since that does not exist.
I'm configuring Spring Security across all my controllers.
I want some method executions to start only when "my system is enabled". This information is accessible from all over the controllers via a specific static method (I can make it non-static).
My point is that I want to avoid making an explicit check in java code at the beginning of every method.
How can I get there via Spring Security?
One approach is to use a handler interceptor.
Here is general idea:
(1) Configure url patterns which you want to block:
<util:list id="sysEnableCheckUrlPatterns" value-type="java.lang.String">
<beans:value>/module1/**</beans:value>
<beans:value>/module2/**</beans:value>
</util:list>
(2) Write an interceptor:
public class SysEnableCheckInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
/*
If system enabled then return true. Otherwise return false (and optionally write something in response)
*/
}
}
(3) Configure that interceptor. In 3.1 you can do it as follows:
#Configuration
public class AppConfig extends WebMvcConfigurerAdapter {
#Resource(name="sysEnableCheckUrlPatterns")
/* or use #Autowired or #Inject if you like */
private String[] sysEnableCheckUrlPatterns;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SysEnableCheckInterceptor()).addPathPatterns(sysEnableCheckUrlPatterns);
}
}
You can use SPEL (Spring Expression Language) in a security annotation.
See http://static.springsource.org/spring-security/site/docs/3.0.x/reference/el-access.html