Spring security: How can I enable anonymous for some matchers, but disable that for the rest? - spring

I am trying to enable the anonymous access to some part of my rest api, but disable that to the rest.
I tried config looks like:
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anonymous().and()
.antMatchers(SOME_URL).authenticated()
.and()
.anoymous().disable()
.antMatchers(OTHER_URL).authenticated();
}
But later, I realized that the later anonymous().disable will cover the previous setting.
So is anyone can give me some suggestion that how can I enable the anonymous for part of my url?
Many thanks!!!

You can define a RequestMatcher, one for public urls and other for protected urls. Then, override the configure method which accepts WebSecurity as param. In this method, you can configure web to ignore your public urls.
private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/public/**")
);
private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIC_URLS);
#Override
public void configure(final WebSecurity web) {
web.ignoring().requestMatchers(PUBLIC_URLS);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(STATELESS)
.and()
.exceptionHandling()
// this entry point handles when you request a protected page and you are not yet
// authenticated
.defaultAuthenticationEntryPointFor(forbiddenEntryPoint(), PROTECTED_URLS)
.anyRequest()
.authenticated();
// and other clauses you would like to add.
}

Related

Springboot configuration 401 Unauthorized

I have this configure method and i want to make user be able to register but i get 401 Unathorized. It is caused by the .apply(**) and i am not able to do it.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/register").permitAll()
.antMatchers("/auth/signin").permitAll()
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider, securityUtils));
}
JwtConfigurer.class
public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final JwtTokenProvider jwtTokenProvider;
private final SecurityUtils securityUtils;
public JwtConfigurer(JwtTokenProvider jwtTokenProvider, SecurityUtils securityUtils) {
this.jwtTokenProvider = jwtTokenProvider;
this.securityUtils = securityUtils;
}
#Override
public void configure(HttpSecurity http) {
JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider, securityUtils);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
So when request is sent to /auth/register i dont want to add .apply(**). Do u have any suggestion please?
In your class that extends WebSecurityConfigurerAdapter where your http configure() method with .apply() is written, you can use the following to tell Spring Boot to bypass or ignore the filter if encountered with the uri for user registration.
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/auth/register");
}
Edit: Since you are facing the exception:
Cross-origin Request Blocked (Reason: CORS header ‘Access-Control-Allow-Origin’ missing and Reason: CORS request did not succeed)
it means that your OPTIONS request (preflight request) is failing. Are you using a different project as Front End for your application? If yes, you will need to specify in your spring boot configuration to allow origin and allow the specified methods from that particular origin.
Please refer to this official documentation to learn how to do that. You can enable Cors at Controller level or at global level. This StackOverflow thread should also be helpful in doing the implementation in case you are unable to proceed.

Custom Authentication Entrypoint not being called on failed Authentication

I have setup an OAUTH Authorization server that's supposed to allow clients request for tokens. It's also supposed to allow admin users carry out other operations.
In my Web Security Configuration:
#Configuration
#EnableWebSecurity
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
private #Autowired CustomAuthenticationProvider authenticationProvider;
private #Autowired CustomAuthenticationEntryPoint entryPoint;
#Override
#Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().httpBasic().and().cors().and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(entryPoint)
.defaultAuthenticationEntryPointFor(entryPoint, new AntPathRequestMatcher("/api/v1/**"));
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
Ideally, when an admin user tries to call any endpoint under "/api/v1/**", they should be authenticated - and in fact, they are.
The issue now is, when authentication fails, the authentication entry endpoint is ignored. I don't understand why this is.
I even included the "default authentication entry point for" just to see if that would help, but it didn't.
Please, how do I resolve this?
After playing around with the http security configuration, I took inspiration from this article (https://www.baeldung.com/spring-security-basic-authentication) and changed it to:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic().authenticationEntryPoint(entryPoint);
}
Honestly, I don't know why what I had before wasn't working. Plenty of people have posted that as the solution to problems about entry end points. But I guess maybe something has changed in Spring that I'm not aware of.

obtain request parameter in Spring security Filter

Can someone help in in obtaining request parameter
in WebsecurityConfig Httpsecurity configure method ? I need to extract the request parameter in the below case acr=loa3 that is coming from request
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.maximumSessions(1)
.expiredUrl(this.uiUri + "/expired")
.maxSessionsPreventsLogin(true)
.and()
.invalidSessionUrl(this.uiUri + "/expired")
.and()
.csrf().disable().cors()
.and()
.authorizeRequests()
.antMatchers("/expired").permitAll()
.anyRequest().authenticated()
.and()
//Can some one help me here on how to extract request param coming in the url for example xyz.com/login?acr=loa3 ? I need to send that as acr value before the configureOIDCfilter executes
.addFilterBefore(configureOIDCfilter(http, acrValue),
AbstractPreAuthenticatedProcessingFilter.class)
.exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint(this.redirectUri));
}
}
#Bean
public OIDCAuthenticationFilter configureOIDCfilter(HttpSecurity http, String acrValue) throws Exception {
OIDCAuthenticationFilter filter = new OIDCAuthenticationFilter();
StaticSingleIssuerService issuerService = new StaticSingleIssuerService();
issuerService.setIssuer(issuerUrl);
filter.setServerConfigurationService(new DynamicServerConfigurationService());
StaticClientConfigurationService clientService = new StaticClientConfigurationService();
RegisteredClient client = new RegisteredClient();
client.setClientId(clientId);
client.setDefaultACRvalues(ImmutableSet.of(acrValue));
return filter;
}
What you showed in your code is configuration. This is done at startup time and cannot catch any request parameters at this time. However, if you want to need to do something by request, you may want to implement a filter as I wrote in my recent blog post.
You could extend from a filter like this:
public class MyAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public MyAuthenticationFilter(AuthenticationManager authenticationManager) {
this.setAuthenticationManager(authenticationManager);
}
}
Then, try to find what methods you want to override. In example:
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
...
}
In the above method you can access the http request parameters.
This filter needs to be added to your configuration as well:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilter(new MyAuthenticationFilter()).
}
A filter will be called for any request and is the only way to receive request parameters (to my knowledge).

Spring security: Apply filter to only an endpoint

Currently, I've two kind of endpoints into my service:
/portal/**: I need to add a filter PortalAuthorizationFilter
The other ones: I need to add a filter OthersAuthorizationFilter
It's important that OthersFilter has not to be applied on /portal/** calls.
I've created a WebSecurityConfigurerAdapter. My current code is:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new JWTExceptionHandlerFilter(objectMapper, messageSource), BasicAuthenticationFilter.class)
.cors().and()
.csrf().disable()
.antMatcher("/portal/**")
.addFilter(new PortalAuthorizationFilter())
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new OthersAuthorizationFilter());
}
}
I've debugged this code when a call is made to /portal/**, PortalAuthorizationFilter is reached, but then OthersAuthorizationFilter is reached as well.
I don't quite figure out how to solve it.
Any ideas?
You need to create another configuration class which will also extend WebSecurityConfigurerAdapter, there you match your url with antMatchers and add your filter. Whichever pattern match will run that security configuration
Read this post
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/portal/**").... //the configuration only
//applies when sb hitting /portal/**
}
}
if you want another configuration for another url you need overwrite another WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
#Order(101) //#Order(100) is default
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http... //without antMatcher(...) default will be used "/**",
//so it take all request. So the order of this class should be
// higher
}
}
If you want use filter approach (.addFilter(new OthersAuthorizationFilter()); )then in your doFilter method you should implement:
doFilter(...) {
if(match(request, "/portal/**")
....
}
Unfortunately AuthenticationProvider will not give you such possibility, it doesn't know about url, just credentials. If you want more read spring-security-architecture.
But I thing you want to delegate authorization
Another option is to use a delegate pattern. Imagine if you have a filter that looks like this
public class PickAndChooseFilter extends OncePerRequestFilter {
private AntPathRequestMatcher matcher = new AntPathRequestMatcher("/portal/**");
private final Filter portalAuthorizationFilter;
private final Filter othersAuthorizationFilter;
public PickAndChooseFilter(Filter portalAuthorizationFilter, Filter othersAuthorizationFilter) {
this.portalAuthorizationFilter = portalAuthorizationFilter;
this.othersAuthorizationFilter = othersAuthorizationFilter;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (matcher.matches(request)) {
portalAuthorizationFilter.doFilter(request, response, filterChain);
} else {
othersAuthorizationFilter.doFilter(request, response, filterChain);
}
}
}
then instead of
.addFilter(new PortalAuthorizationFilter())
.addFilter(new OthersAuthorizationFilter())
you'd simply have
.addFilter(new PickAndChooseFilter(
new PortalAuthorizationFilter(),
new OthersAuthorizationFilter()
)

Spring Security Java Config - dynamic IP list for a Request URL

I have two configuration. The first would like to achieve that all requests from(/api/**) must come only from a determined ip.
like Following...
.authorizeRequests().antMatchers("/api/**").hasIpAddress("dynamic List of IPs");
It should be checked whether the IP is stored in the database, otherwise the access is to be denied.
And the secound config takes care of the rest.
#EnableWebSecurity
public class AppSecurityConfig {
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new CustomUserDetailsService()).passwordEncoder(new Md5PasswordEncoder());
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.headers().disable()
.authorizeRequests().antMatchers("/api/**").hasIpAddress("dynamic List of IPs");
}
}
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().maximumSessions(1)
.expiredUrl("/error/expired.xhtml").and()
.invalidSessionUrl("/Anmeldung.xhtml?check=invalid");
http
.csrf().disable()
.headers().disable()
.formLogin().loginPage("/Anmeldung/").loginProcessingUrl("/j_spring_security_check").successHandler(new CustomAuthenticationSuccessHandler())
.failureUrl("/Anmeldung.xhtml?check=error").usernameParameter("j_username").passwordParameter("j_password")
.and()
.exceptionHandling().accessDeniedPage("/error/403.xhtml")
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/Anmeldung.xhtml?check=logout").invalidateHttpSession(false).deleteCookies("JSESSIONID").permitAll();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry interceptUrlRegistry = http.authorizeRequests();
interceptUrlRegistry.antMatchers("/Administrator/*").hasAnyAuthority("ROLE_ADMIN");
interceptUrlRegistry.antMatchers("/*").hasAnyAuthority("ROLE_USER");
interceptUrlRegistry.antMatchers("/Anmeldung/index.xhtml").anonymous();
interceptUrlRegistry.antMatchers("/template/*").denyAll();
interceptUrlRegistry.antMatchers("/resources/**").permitAll();
}
}
}
Thanks for your help.
You can dynamically configure httpsecurity object inside for loop like the code referenced below.
for (Entry<String, String> entry : hasmapObject) {
String url = entry.getKey().trim();
String ips= entry.getValue().trim();
http.authorizeRequests().and().authorizeRequests().antMatchers(url).hasIpAddress(ips);
}
This worked for me. The hashmap object had the dynamic list of url's and their corresponding ips to give access.
"http.authorizeRequests().and()" this and() is needed to indent like we use in xml configuration to configure http child elements in XML.
Please let me know if this helps.

Resources