How to allow specific requests when using spring-security - java

I've a scenario where I need to let some request (let's say request method is GET) so that no 401 error can be thrown.
Below is my Spring Security configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/bower_components/**")
.antMatchers("/fonts/**")
.antMatchers("/images/**")
.antMatchers("/scripts/**")
.antMatchers("/styles/**")
.antMatchers("/views/**")
.antMatchers("/i18n/**")
.antMatchers("/swagger-ui/**")
.antMatchers("/app/rest/register")
.antMatchers("/app/rest/activate");
}
}
And this is my ResourceServerConfigurerAdapter implementation:
How can I allow requests?

There is a method called requestMatchers where you can call it with one or more RequestMatcher implementaions.
public void configure(HttpSecurity http){
.....
web.ignoring().requestMatchers(new MethodTypeRequestMatcher(RequestMethod.GET));
.....
}
And you can define your implementation:
public class MethodRequestMatcher implements RequestMatcher {
private RequestMethod method;
public MethodRequestMatcher(RequestMethod method) {
this.method = method;
}
#Override
public boolean matches(HttpServletRequest request) {
if (method == null) {
return false;
}
return request.getMethod().equals(method.name());
}
}

I think you can try like follows:
<code>
#Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.httpStrictTransportSecurity()
.xssProtection()
.frameOptions()
.and().authorizeRequests()
// PERMIT ALL
.antMatchers("/home").permitAll()
// UNAUTHENTICATED USER
.antMatchers("/ForgetPassword").anonymous()
// TO SPECIFIC PERSON
.antMatchers("/Report").access("hasRole('ADMIN') or hasRole('S_USER')");
}
</code>

Related

Spring security returns 401, On sending date as input to the rest controller

#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsInfoService userDetailsService;
#Autowired
private CustomLoginAuthenticationProvider authenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
auth.userDetailsService(userDetailsService);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/api/user/authenticate/**")
.antMatchers("/api/user/**")
.antMatchers("/api/master/**")
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/* #EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}*/
}
This is my spring security configuration in the application.
It works fine most of the scenarios, but not able to track one issue.
Whenever I call a web service with a date format spring security returns 401 error.
http://localhost:9190/nazouki/api/master/create
with input
{"orgId":"11","birthDate":"2020-04-20T20:00:00.000Z"}
without date it works fine.
Controller code
#RequestMapping(value = "master/create", method = RequestMethod.POST)
public #ResponseBody ResultDecorator createMaster(#RequestBody TuMasterDto tuMasterDto){ handler.resolveResult(masterService.createMaster(tuMasterDto), OperationEnum.SAVE);
}
Exception has been thrown from the CustomAuthenticationEntryPoint in the spring security.
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Logger log = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException ae) throws IOException, ServletException {
log.info("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}
We can't say for sure unless we also have a look at the handler.resolveResult() and the masterService.createMaster() methods.
My initial assumption would be that you have a GlobalMethodSecurity configuration on one or both of these methods (or a code path that is used by them), and since you ignore the security for the path web.ignoring().antMatchers("/api/master/**"), there is no SecurityContext created and therefore you get that response.
As to why it behaves differently when you add the date than without, I'm not so sure myself. Maybe you have 2 implementations? something like this
public void handleRequest(Dto dto) {
if(dto.getDate() != null) {
doSomethingWithTheDate(dto.getDate());
}
// handle normally
}
#PreAuthorize("hasRole('ROLE_USER')")
public void doSomethingWithTheDate(Date date) {
...
}

Principal argument null for anonymous user in ResourceServer

I have an extremely simple sample app here: https://github.com/timtebeek/anonymous-principal
Relevant bits copied below:
ResourceConfig.java
#Configuration
#EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/**").permitAll()
.anyRequest().denyAll();
// Anonymous user should authenticate as guest for authorization
http.anonymous().principal("guest");
}
#Override
public void configure(final ResourceServerSecurityConfigurer resources) {
resources.resourceId("myresource");
}
}
DemoApplication
#SpringBootApplication
#RestController
#SuppressWarnings("static-method")
public class DemoApplication {
public static void main(final String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#RequestMapping(value = "/principal", method = RequestMethod.GET)
public String get(final Principal user) {
Assert.notNull(user);
return user.getName();
}
#RequestMapping(value = "/authprincipal", method = RequestMethod.GET)
public String get(#AuthenticationPrincipal final String user) {
Assert.notNull(user);
return user;
}
#RequestMapping(value = "/authentication", method = RequestMethod.GET)
public String get() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Assert.notNull(auth);
return auth.getName();
}
}
In this setup both /authprincipal and /authentication work, but /principal fails when the user is not authenticated, as the principal argument is null. I'd wanted to use the plain Principal rest method argument with my anonymous users as well, as that gives me the cleanest code.
What can I do to make Principal argument in my rest methods work for anonymous users?

Not able to refresh spring oauth2 token with multiple authentication provider

I'm trying to create an oauth2-enabled auth server which is able to authenticate users with two authentication providers: the former is in-memory (for default user-passwords) the latter is an external LDAP server (by now i'm using the example from gs-authenticating-ldap-complete).
I'm able to successfully retrieve an access token for any user, but i'm only able to use the refresh token for retrieving a new token for any user that is registered in the LDAP server. While everything is fine if I try to refresh an in-memory user's token, with the LDAP ones I get: 401 Unauthorized { "error": "unauthorized", "error_description": "ben" } where "ben" is the user id.
As far as I know (after some debugging) the exception occurs in DefaultTokenServices.java:150.
In the following I report the configuration classes I'm using.
#Configuration
#EnableWebSecurity
#Order(6)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().csrf().disable();
}
#Configuration
protected static class DefaultUsersAuthConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN").and().withUser("guest")
.password("guest").roles("USER");
}
}
#Configuration
protected static class LDAPAuthConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().userDnPatterns("uid={0},ou=people").groupSearchBase("ou=groups")
.userDetailsContextMapper(new MyLdapUserDetailsMapper()).contextSource()
.ldif("classpath:test-server.ldif");
}
}
protected static class MyLdapUserDetailsMapper extends LdapUserDetailsMapper {
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection<? extends GrantedAuthority> authorities) {
final UserDetails originalUser = super.mapUserFromContext(ctx, username, authorities);
final Set<GrantedAuthority> newAuth = new HashSet<>(originalUser.getAuthorities());
newAuth.add(new SimpleGrantedAuthority("ROLE_EXTRA_ROLE"));
return new User(originalUser.getUsername(), originalUser.getPassword(), originalUser.isEnabled(),
originalUser.isAccountNonExpired(), originalUser.isCredentialsNonExpired(),
originalUser.isAccountNonLocked(), newAuth);
}
}
}
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends OAuth2AuthorizationServerConfiguration {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("acme").secret("acmesecret")
.authorizedGrantTypes("password", "refresh_token", "client_credentials")
.scopes("read", "write", "openid").autoApprove(true);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore);
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests().antMatchers("/me").authenticated();
// #formatter:on
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore);
}
}
}
I'm using spring-boot 1.3.2.RELEASE. What am I missing?

Use #Autowired with a Filter configured in SpringBoot

I need to use autowired in a filter. So i annotate my filter class using #Component,
import org.springframework.web.filter.GenericFilterBean;
#Component
public class TokenAuthorizationFilter extends GenericFilterBean {
#Autowired
public EnrollCashRepository enrollCashRepository;
}
Then i add my filter as below in SecurityConfig,
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity.ignoring().antMatchers(HttpMethod.GET, "/health");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new TokenAuthorizationFilter(), BasicAuthenticationFilter.class);
http.authorizeRequests().antMatchers("/api/**").authenticated();
}
My problem is my filter get invoked twice with the #Component annotation. If i remove the #Component annotation it only invoke once.
Then i add below as a fix in my Spring boot main class. Then i comment the line of addFilterBefore in SecurityConfig.
#Bean
public FilterRegistrationBean tokenAuthFilterRegistration() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new PITokenAuthorizationFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
But then my filter get invoked once. But even i make the setEnabled true or false, i get a 403 Forbiddon Error when i invoke my rest api, http://localhost:8080/api/myservice
How can i fix this situation where i can use #Autowired in my Spring Filter?
Edit: Add controller and Filter class,
#RestController
#RequestMapping(value = "/api")
public class SpringToolController {
#RequestMapping(value = "/myservice", method = RequestMethod.GET)
public HttpEntity<String> myService() {
System.out.println("-----------myService invoke-----------");
return new ResponseEntity<String>(HttpStatus.OK);
}
}
public class TokenAuthorizationFilter extends GenericFilterBean {
public TokenAuthorizationFilter(EnrollCashRepository enrollCashRepository) {
this.enrollCashRepository = enrollCashRepository;
}
public EnrollCashRepository enrollCashRepository;
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
System.out.println("before PITokenAuthorizationFilter");
chain.doFilter(servletRequest, servletResponse);
System.out.println("after PITokenAuthorizationFilter");
}
public EnrollCashRepository getEnrollCashRepository() {
return enrollCashRepository;
}
public void setEnrollCashRepository(EnrollCashRepository enrollCashRepository) {
this.enrollCashRepository = enrollCashRepository;
}
}
Remove your FilterRegistrationBean and initialize TokenAuthorizationFilter inside your SecurityConfig like this:
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public EnrollCashRepository enrollCashRepository;
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity.ignoring().antMatchers(HttpMethod.GET, "/health");
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.addFilterBefore(tokenAuthorizationFilter(), BasicAuthenticationFilter.class);
http.authorizeRequests().antMatchers("/api/**").authenticated();
}
private TokenAuthorizationFilter tokenAuthorizationFilter()
{
return new TokenAuthorizationFilter(enrollCashRepository);
}
}
Remove #Autowired and #Component annotation and set your EnrollCashRepository with constructor injection:
import org.springframework.web.filter.GenericFilterBean;
public class TokenAuthorizationFilter extends GenericFilterBean {
private final EnrollCashRepository enrollCashRepository;
public TokenAuthorizationFilter(EnrollCashRepository enrollCashRepository)
{
this.enrollCashRepository = enrollCashRepository
}
}
I Added a Test Filter to my working class now and it worked fine. Here are the codes related to it.
Filter
#Component
public class TestFilter extends GenericFilterBean {
private static final Logger logger = LoggerFactory.getLogger(TestFilter.class);
#Autowired
UserService userService;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
logger.error("=====================AWESOME=======================");
chain.doFilter(request, response);
userService.activate("123"); //this works
}
}
App Security Config
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private TestFilter testFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
//loginFailureHandler.setDefaultFailureUrl("/login?error=true");
http.addFilterBefore(testFilter, BasicAuthenticationFilter.class);
//Http other config here.
}
}
App Config
#Configuration
#ImportResource({
"classpath*:/context.xml"
})
#PropertySources(
#PropertySource({
"classpath:/application.yml"
})
)
#Import({AppSecurityConfig.class, WebConfig.class,TestFilter.class})
public class AppConfig {
}

How to add filter after the HTTP BasicAuthenticationFilter when using #EnableAuthorizationServer

I am trying to go over the following documentation: https://github.com/spring-projects/spring-security-oauth/blob/f25592e682303b0cf89e1d7555174bac18e174df/docs/oauth2.md#mapping-user-roles-to-scopes
In the documentation, it says in order to map user roles to scopes, along with setting the checkUserScopes=true in the DefaultOAuth2RequestFactory, we need to add the TokenEndpointAuthenticationFilter filter after the HTTP BasicAuthenticationFilter. I was wondering how that could be done.
Here is what my AuthorizationServer looks like:
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends
AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private OAuth2RequestFactory requestFactory;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager);
endpoints.requestFactory(requestFactory);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.withClientDetails(clientDetailsService());
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.checkTokenAccess("isAuthenticated()");
}
#Bean
public ClientDetailsService clientDetailsService() {
Map<String, ClientDetails> clientDetailsStore = new HashMap<String, ClientDetails>();
Collection<String> scope = new HashSet<String>();
scope.add("user");
scope.add("admin");
Collection<String> authorizedGrantTypes = new HashSet<String>();
authorizedGrantTypes.add("password");
authorizedGrantTypes.add("refresh_token");
BaseClientDetails clientDetails = new BaseClientDetails();
clientDetails.setClientId("client");
clientDetails.setClientSecret("secret");
clientDetails.setScope(scope);
clientDetails.setAuthorizedGrantTypes(authorizedGrantTypes);
clientDetailsStore.put("client", clientDetails);
InMemoryClientDetailsService clientDetailsService = new InMemoryClientDetailsService();
clientDetailsService.setClientDetailsStore(clientDetailsStore);
return clientDetailsService;
}
#Bean
public OAuth2RequestFactory requestFactory() {
DefaultOAuth2RequestFactory requestFactory =
new DefaultOAuth2RequestFactory(clientDetailsService());
requestFactory.setCheckUserScopes(true);
return requestFactory;
}
}
Also, it would be fantastic to provide a sample CURL on how we can test the grant-type password.
Appreciate any help!
Instead of using #EnableAuthorizationServer you should be able to extend AuthorizationServerSecurityConfiguration and include that in your Spring configuration. E.g.
#Configuration
public class OAuth2Config extends AuthorizationServerSecurityConfiguration {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.addFilterAfter(myFilter(), BasicAuthenticationFilter.class);
}
}
You can add also add additional filters via the AuthorizationServerSecurityConfigurer, though they come before Basic auth, not after.
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.addTokenEndpointAuthenticationFilter(myFilter());
security.checkTokenAccess("isAuthenticated()");
}
Adds a new custom authentication filter for the TokenEndpoint. Filters will be set upstream of the default BasicAuthenticationFilter.

Categories