Showing posts with label spring-security. Show all posts
Showing posts with label spring-security. Show all posts

Monday, October 8, 2018

Spring secure endpoint with only client credentials (Basic)

Leave a Comment

I have oauth2 authorization server with one custom endpoint (log out specific user manually as admin) I want this endpoint to be secured with rest client credentials (client id and secret as Basic encoded header value), similar to /oauth/check_token.

This endpoint can be called only from my resource server with specific scope.

  1. I need to check if the client is authenticated.
  2. I would like to be able to add @PreAuthorize("#oauth2.hasScope('TEST_SCOPE')")on the controller`s method.

I could not find any docs or way to use the Spring`s mechanism for client authentication check.

EDIT 1

I use java config not an xml one

1 Answers

Answers 1

@PreAuthorize("#oauth2.hasScope('TEST_SCOPE')") On the controller method should be sufficiënt. If the client is not authenticated, no scope is available and the scope check will fail.

If you want, you can use the Spring Security expression @PreAuthorize("isAuthenticated()") to check if a client is authenticated: https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/#el-common-built-in

You could also configure the HttpSecurity instead of working with @PreAuthorize

Read More

Saturday, September 8, 2018

Using multiple OAuth2 clients in single browser session using Spring boot

Leave a Comment

We have Multi tenant WebApp designed using Spring Boot + Spring Security. This app is used to manage certain resources in Azure. User login into our WebApp using OAuth2.0 and can access Azure resources through our app.

Now we need to allow multiple users to login into our app in single browser session. So basically user (user 1) will use credentials1 to login to access resources allowed by these credentials. Then user will use credentials2 (basically another users credentials lets call it user2) to login into same browser page. There will be two active users in same session. User should be able to switch between these accounts.

Once user login into our app, we instantiate RestTemplate (using credentials entered) to access Azure resources.

Either we can have single JSession id mapped to multiple RestTemplate or multiple JSession ID (within single JSession cookie) to mapped to individual RestTemplate. We can have request parameter indicating which RestTemplate to use.

We have used SpringSecurity to get access token. This access token is then used in RestTemplate and used for accessing Azure resources.

1 Answers

Answers 1

"Now we need to allow multiple users to login into our app in single browser session"

Is this approach secure, at all? I mean, having two users using the same browser and sharing information isn't recommended.

"Either we can have single JSession id mapped to multiple RestTemplate or multiple JSession ID (within single JSession cookie) to mapped to individual RestTemplate"

I never saw this kind of approach. Get Google as an example -- you can switch profiles, but need to log in.

If you really need to do it, there's an out of the box solution for Chrome, Firefox and Opera called SessionBox, that enables session switch within the same browser. Otherwise, two common solutions are:

  • Use two different browsers (e.g. Chrome and Firefox)
  • Use incognito mode
Read More

Tuesday, September 4, 2018

No qualifying bean of type 'org.springframework.cloud.bootstrap.encrypt.RsaProperties'

Leave a Comment

I am getting a nosuchbean exception as the title suggests, just adding text here to satisfy the mostly code thing.

Have put unlimited crypto jars in jre\lib\security

Key store created in application at src\main\resources and is called config-server.jks

application.properties (tried both key-stores location prop definitions)

server.port=8888 spring.cloud.config.server.git.uri=ssh://git@v00bitbucket:7999/proj/config-server.git spring.cloud.config.server.git.clone-on-start=true security.user.name=Joe security.user.password={bcrypt}$2a$10$7H8tnjyf/Mn90eAZADruterXJ.t.GQP4WgRIZ8cwnRsMmhZhCtS1a #encrypt.key-store.location=classpath:/config-server.jks encrypt.key-store.location=file://C:/myAppDir/config- server/src/main/resources/config-server.jks encrypt.key-store.password=my-s70r3-s3cr3t encrypt.key-store.alias=config-server-key encrypt.key-store.secret=my-k34-s3cr3t 

using java 1.8.0_77

@RunWith(SpringRunner.class) @SpringBootTest public class ConfigServerApplicationTests {         @Test     public void contextLoads() {     } }       @SpringBootApplication @EnableConfigServer public class ConfigServerApplication {     public static void main(String[] args) {         SpringApplication.run(ConfigServerApplication.class, args);     } }  @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter {     @Value("${security.user.name}")     private String authUser;     @Value("${security.user.password}")     private String authPassword; // this password is encoded     @Autowired     protected void configure(AuthenticationManagerBuilder auth) throws Exception {         auth.inMemoryAuthentication().passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder())         .withUser(authUser).password(authPassword).roles("User");     }      @Override     protected void configure(HttpSecurity http) throws Exception {         http.authorizeRequests().anyRequest().fullyAuthenticated();         http.httpBasic();         http.csrf().disable();     } } 

here is the pom {

<parent>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-parent</artifactId>     <version>2.0.4.RELEASE</version>     <relativePath/> <!-- lookup parent from repository --> </parent>  <properties>     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>     <java.version>1.8</java.version>     <spring-cloud.version>Finchley.SR1</spring-cloud.version> </properties>  <dependencies>     <dependency>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-web</artifactId>     </dependency>     <dependency>         <groupId>org.springframework.cloud</groupId>         <artifactId>spring-cloud-config-server</artifactId>     </dependency>      <dependency>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-tomcat</artifactId>         <scope>provided</scope>     </dependency>     <dependency>         <groupId>org.springframework.security</groupId>         <artifactId>spring-security-config</artifactId>     </dependency>     <dependency>         <groupId>org.springframework.security</groupId>         <artifactId>spring-security-web</artifactId>     </dependency>     <dependency>         <groupId>org.springframework.security</groupId>         <artifactId>spring-security-rsa</artifactId>     </dependency>     <dependency>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-test</artifactId>         <scope>test</scope>     </dependency> </dependencies> 

}

1 Answers

Answers 1

Just ran into this problem too.

My best guess is that it is a bug in the latest version of Spring Cloud. I will open an issue on Spring Cloud project and link it here once finished.

I am using application.yml, not application.properties.

When you put any config for encrypt: * in application yml, it will give you this error. As a work-a-round, I tried putting the encrypt:* config in bootstrap.yml

After that, the Spring Boot app started successfully and it will have the RsaProperties Bean :)

Hope this helps!

Read More

Sunday, August 12, 2018

Spring Boot 2 upgrade. permit All works with .anonymous().and() .. but why?

Leave a Comment

I had migrated Oauth2 code from Spring Boot 1.3.2 T Spring Boot 2.0.2.

The below code to permit "/auth" endpoint is not working in springBoot 2

public void configure(HttpSecurity httpSecurity) throws Exception {         httpSecurity                                 .exceptionHandling()                 .authenticationEntryPoint(customAuthenticationEntryPoint)                 .and()                 .antMatchers("/auth/**").permitAll()                 .anyRequest()                 .authenticated();      } 

/auth endpoint return 401 response.

After research I hadd added

.anonymous().and()

and it working as expected now. /auth return result with out token.

I am not able to figure out the reason, what changed in spring boot 2. ?

0 Answers

Read More

Tuesday, August 7, 2018

Configuring security in a Spring Boot application

Leave a Comment

I'm upgrading an application to Spring Boot 2.0.3.

But my login request is unauthorized:

curl -H "Accept:application/json" -H "Content-Type: application/json" "http://localhost:8080/api/users/login" -X POST -d "{ \"email\" : \"myemail@somedomain.com\", \"password\" : \"xxxxx\" }" -i 

The response is a 401 Unauthorized access. You failed to authenticate.

It is given by my custom entry point:

@Component public final class RESTAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {      private static Logger logger = LoggerFactory.getLogger(RESTAuthenticationEntryPoint.class);      @Override     public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {         logger.debug("Security - RESTAuthenticationEntryPoint - Entry point 401");         response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);         response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized access. You failed to authenticate.");     }      @Override     public void afterPropertiesSet() throws Exception {         setRealmName("User REST");         super.afterPropertiesSet();     }  } 

The debugger shows the authenticate method of my CustomAuthenticationProvider is not called as I expect it to be:

@Component public class CustomAuthenticationProvider implements AuthenticationProvider {      @Autowired     CredentialsService credentialsService;      @Override     public Authentication authenticate(Authentication authentication) throws AuthenticationException {         String email = authentication.getName();         String password = authentication.getCredentials().toString();         List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<SimpleGrantedAuthority>();         User user = null;         try {             user = credentialsService.findByEmail(new EmailAddress(email));         } catch (IllegalArgumentException e) {             throw new BadCredentialsException("The login " + email + " and password could not match.");                      }         if (user != null) {             if (credentialsService.checkPassword(user, password)) {                 grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));                 return new UsernamePasswordAuthenticationToken(email, password, grantedAuthorities);             } else {                 throw new BadCredentialsException("The login " + user.getEmail() + " and password could not match.");                            }         }         throw new BadCredentialsException("The login " + authentication.getPrincipal() + " and password could not match.");     }      @Override     public boolean supports(Class<?> authentication) {         return authentication.equals(UsernamePasswordAuthenticationToken.class);     }  } 

But the filter is exercised and a null token is found:

@Component public class AuthenticationFromTokenFilter extends OncePerRequestFilter {      @Autowired     private TokenAuthenticationService tokenAuthenticationService;      @Override     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)             throws IOException, ServletException {         tokenAuthenticationService.authenticateFromToken(request);         chain.doFilter(request, response);     }  }  @Service public class TokenAuthenticationServiceImpl implements TokenAuthenticationService {      private static Logger logger = LoggerFactory.getLogger(TokenAuthenticationServiceImpl.class);      private static final long ONE_WEEK = 1000 * 60 * 60 * 24 * 7;     private static final String TOKEN_URL_PARAM_NAME = "token";      @Autowired     private ApplicationProperties applicationProperties;      @Autowired     private UserDetailsService userDetailsService;      public void addTokenToResponseHeader(HttpHeaders headers, String username) {         String token = buildToken(username);         headers.add(CommonConstants.AUTH_HEADER_NAME, token);     }      public void addTokenToResponseHeader(HttpServletResponse response, Authentication authentication) {         String username = authentication.getName();         if (username != null) {             String token = buildToken(username);             response.addHeader(CommonConstants.AUTH_HEADER_NAME, token);         }     }      private String buildToken(String username) {         String token = null;         UserDetails userDetails = userDetailsService.loadUserByUsername(username);         if (userDetails != null) {             Date expirationDate = new Date(System.currentTimeMillis() + ONE_WEEK);             token = CommonConstants.AUTH_BEARER + " " + Jwts.builder().signWith(HS256, getEncodedPrivateKey()).setExpiration(expirationDate).setSubject(userDetails.getUsername()).compact();                }         return token;     }      public Authentication authenticateFromToken(HttpServletRequest request) {         String token = extractAuthTokenFromRequest(request);         logger.debug("The request contained the JWT token: " + token);         if (token != null && !token.isEmpty()) {             try {                 String username = Jwts.parser().setSigningKey(getEncodedPrivateKey()).parseClaimsJws(token).getBody().getSubject();                 if (username != null) {                     UserDetails userDetails = userDetailsService.loadUserByUsername(username);                     UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());                     authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));                     SecurityContextHolder.getContext().setAuthentication(authentication);                     logger.debug("Security - The filter authenticated fine from the JWT token");                 }             } catch (SignatureException e) {                 logger.info("The JWT token " + token + " could not be parsed.");             }         }         return null;     }      private String extractAuthTokenFromRequest(HttpServletRequest request) {         String token = null;         String header = request.getHeader(CommonConstants.AUTH_HEADER_NAME);         if (header != null && header.contains(CommonConstants.AUTH_BEARER)) {             int start = (CommonConstants.AUTH_BEARER + " ").length();             if (header.length() > start) {                 token = header.substring(start - 1);             }         } else {             // The token may be set as an HTTP parameter in case the client could not set it as an HTTP header             token = request.getParameter(TOKEN_URL_PARAM_NAME);         }         return token;     }      private String getEncodedPrivateKey() {         String privateKey = applicationProperties.getAuthenticationTokenPrivateKey();         return Base64.getEncoder().encodeToString(privateKey.getBytes());     }  } 

My security configuration is:

@Configuration @EnableWebSecurity @ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.thalasoft.user.rest.security", "com.thalasoft.user.rest.filter" }) public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {      @Autowired     private UserDetailsService userDetailsService;      @Autowired     private AuthenticationFromTokenFilter authenticationFromTokenFilter;      @Autowired     private SimpleCORSFilter simpleCORSFilter;      @Autowired     private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;      @Bean     public AuthenticationManager authenticationManagerBean() throws Exception {         return super.authenticationManagerBean();     }      @Override     protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {         authenticationManagerBuilder.authenticationProvider(new CustomAuthenticationProvider());     }      @Override     protected void configure(HttpSecurity http) throws Exception {         http             .exceptionHandling()             .authenticationEntryPoint(restAuthenticationEntryPoint)         .and()             .csrf().disable()             .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)         .and()             .addFilterBefore(simpleCORSFilter, UsernamePasswordAuthenticationFilter.class)             .addFilterBefore(authenticationFromTokenFilter, UsernamePasswordAuthenticationFilter.class)             .headers().cacheControl().disable().frameOptions().disable()         .and()             .userDetailsService(userDetailsService)             .authorizeRequests()             .antMatchers(RESTConstants.SLASH + UserDomainConstants.USERS + RESTConstants.SLASH + UserDomainConstants.LOGIN).permitAll()             .antMatchers(RESTConstants.SLASH + RESTConstants.ERROR).permitAll()             .antMatchers("/**").hasRole(UserDomainConstants.ROLE_ADMIN).anyRequest().authenticated();     }  } 

The user details service is:

@Component public class UserDetailsServiceImpl implements UserDetailsService {      @Autowired     private CredentialsService credentialsService;      @Override     @Transactional     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {         if (username != null && !username.isEmpty()) {             User user = credentialsService.findByEmail(new EmailAddress(username));             if (user != null) {                 return new UserDetailsWrapper(user);             }         }         throw new UsernameNotFoundException("The user " + username + " was not found.");     }  } 

Why is the custom authentication provider not authenticating the username and password ?

UPDATE: I read something interesting and puzzling in this guide

Note that the AuthenticationManagerBuilder is @Autowired into a method in a @Bean - that is what makes it build the global (parent) AuthenticationManager. In contrast if we had done it this way (using an @Override of a method in the configurer) then the AuthenticationManagerBuilder is only used to build a "local" AuthenticationManager, which is a child of the global one. In a Spring Boot application you can @Autowired the global one into another bean, but you can’t do that with the local one unless you explicitly expose it yourself. 

So, is there anything wrong with my usage of the configure method for setting up the authenticationManagerBuilder.authenticationProvider(customAuthenticationProvider); ? Instead of the above configuration, I tried the following configuration:

@Autowired public void initialize(AuthenticationManagerBuilder authenticationManagerBuilder) {     authenticationManagerBuilder.authenticationProvider(customAuthenticationProvider); } 

But it still didn't exercise the custom authentication provider upon a request.

I also tried to have the filter after as in:

http.addFilterAfter(authenticationFromTokenFilter, UsernamePasswordAuthenticationFilter.class); 

instead of addFilterBefore but it didn't change anything to the issue.

2 Answers

Answers 1

According to me when we implement our own ApplicationFilter by implementing GenericFilterBean we need to check if the token received from the request is valid or not. If it is not valid then we need to dump the token into the security context (for the authentication-provider to pick up). I haven't gone through your filter class. But this worked for me :

@Override     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {         HttpServletRequest httprequset=(HttpServletRequest)request;         String uname=request.getParameter("username");         String pwd=request.getParameter("password");         String role=request.getParameter("role");         List<GrantedAuthority> l = new ArrayList<>();         l.add( new SimpleGrantedAuthority(role.toUpperCase()) );          UsernamePasswordAuthenticationToken token=new UsernamePasswordAuthenticationToken(uname,pwd,l);         token.setAuthenticated(false);          SecurityContextHolder.getContext().setAuthentication(token);          chain.doFilter(httprequset, response);     } 

Answers 2

In WebSecurityConfiguration inside configure(HttpSecurity http) method:

http.authorizeRequests().antMatchers("/api/users/login").permitAll();  http.authorizeRequests().anyRequest().authenticated(); 

Add in the same order.

Explanation: Login and logout requests should be permitted without any authentication

A sample configure method that works is:

http.formLogin().disable().logout().disable().httpBasic().disable();          http.authorizeRequests().antMatchers("/logout", "/login", "/").permitAll();         http.authorizeRequests().anyRequest().authenticated();         http.addFilterBefore(new SomeFilter(), SecurityContextHolderAwareRequestFilter.class);         http.addFilterBefore(new CORSFilter(env), ChannelProcessingFilter.class);          http.addFilterBefore(new XSSFilter(),CORSFilter.class); 
Read More

Thursday, July 26, 2018

How to deny access to all URLs by default?

Leave a Comment

When defining new spring boot REST resources, I tend to forget to also create a spring security configuration for their url patterns.

How can I, by default, deny access to all URLs, and only allow access to explicitly configured URL patterns? (I am using .hasRole for to allow access) I want to avoid as many unintended security holes as possible.

Let's say I have three REST resources: /jobs, /desks and /salary. My current code might look like this:

http.authorizeRequests()      .antMatchers(HttpMethod.GET, "/jobs")     .hasRole("my_user")      .antMatchers(HttpMethod.GET, "/desks")     .hasRole("my_user"); 

But currently, access to url /salary is provided to everyone (because it is not yet configured)!

3 Answers

Answers 1

You can deny all requests by default with: .anyRequest().denyAll() and explicit allow requests with .hasRole

Answers 2

Spring's Expression-Based Access Control has a denyAll expression which always evaluates to false.

So what you can do is use this denyAll method to deny access to everything, and then perhaps allow access to a certain URL(s) via hasRole:

http.authorizeRequests().antMatchers("/admin/**").access("hasRole('ADMIN')").antMatchers("/**").denyAll(); 

So for example, this will allow users with ADMIN access to access any page starting with /admin. And then it will deny access to all other pages. Note that the order is important as if you put in .antMatchers("/**").denyAll() first, it will deny all access and ignore the rest of your expression.

Or alternatively, you could use permitAll() for a certain URL pattern:

http.authorizeRequests().antMatchers("/users/**").permitAll().antMatchers("/**").denyAll(); 

Just to note that you might need to allow access to some way of logging in too, so the system can let someone log in with a specific role, so to combine it all together, and allow everyone to try login, only admin users to access the admin page(s) and deny all others, then something like:

http.authorizeRequests().antMatchers("/login").permitAll().antMatchers("/admin/**").access("hasRole('ADMIN')").antMatchers("/**").denyAll(); 

Answers 3

anyRequest().denyAll() to deny all requests by default and .hasRole to explicit allow requests

Read More

Wednesday, July 11, 2018

How to configure Resource Server in Spring Security for it to use additional information in JWT token

Leave a Comment

I have an oauth2 jwt token server configured to set additional info about the user authorities.

@Configuration @Component public class CustomTokenEnhancer extends JwtAccessTokenConverter {      CustomTokenEnhancer(){         super();     }      @Override     public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {         // TODO Auto-generated method stub         MyUserDetails user = (MyUserDetails) authentication.getPrincipal();         final Map<String, Object> additionalInfo = new HashMap<>();         @SuppressWarnings("unchecked")         List<GrantedAuthority> authorities= (List<GrantedAuthority>) user.getAuthorities();         additionalInfo.put("authorities", authorities);          ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);          return accessToken;     }  } 

I am not sure how to configure my resource server to extract the user authorities set by the oauth2 server and use that authority to be used for @Secured annotated controllers in Spring Security framework.

My Auth server configuration looks like this:

@Configuration @EnableAuthorizationServer public class OAuth2Config extends AuthorizationServerConfigurerAdapter {      @Value("${config.oauth2.privateKey}")     private String privateKey;      @Value("${config.oauth2.publicKey}")     private String publicKey;      @Value("{config.clienturl}")     private String clientUrl;      @Autowired     AuthenticationManager authenticationManager;      @Bean     public JwtAccessTokenConverter customTokenEnhancer(){          JwtAccessTokenConverter customTokenEnhancer = new CustomTokenEnhancer();         customTokenEnhancer.setSigningKey(privateKey);          return customTokenEnhancer;     }      @Bean     public JwtTokenStore tokenStore() {         return new JwtTokenStore(customTokenEnhancer());     }       @Override     public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {         oauthServer                 .tokenKeyAccess("isAnonymous() || hasRole('ROLE_TRUSTED_CLIENT')") // permitAll()                 .checkTokenAccess("hasRole('TRUSTED_CLIENT')"); // isAuthenticated()     }       @Override     public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {         endpoints           .authenticationManager(authenticationManager)         .tokenStore(tokenStore())         .accessTokenConverter(customTokenEnhancer()) ;     }      @Override     public void configure(ClientDetailsServiceConfigurer clients) throws Exception {          String url = clientUrl;          clients.inMemory()           .withClient("public")          .authorizedGrantTypes("client_credentials", "implicit")         .scopes("read")         .redirectUris(url)          .and()           .withClient("eagree_web").secret("eagree_web_dev")         //eagree_web should come from properties file?         .authorities("ROLE_TRUSTED_CLIENT")          .authorizedGrantTypes("client_credentials", "password", "authorization_code", "refresh_token")         .scopes("read", "write", "trust")          .redirectUris(url).resourceIds("dummy");     } } 

And my Resource Server configuration looks like this:

@Configuration @EnableResourceServer public class ResourceServerConfiguration  extends ResourceServerConfigurerAdapter {        @Value("{config.oauth2.publicKey}")     private String publicKey;      @Autowired     CustomTokenEnhancer tokenConverter;      @Autowired     JwtTokenStore jwtTokenStore;      @Bean     public JwtTokenStore jwtTokenStore() {         tokenConverter.setVerifierKey(publicKey);         jwtTokenStore.setTokenEnhancer(tokenConverter);         return jwtTokenStore;     }      @Bean     public ResourceServerTokenServices defaultTokenServices() {         final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();         defaultTokenServices.setTokenEnhancer(tokenConverter);         defaultTokenServices.setTokenStore(jwtTokenStore());         return defaultTokenServices;     }       @Override     public void configure(HttpSecurity http) throws Exception {         super.configure(http);         // @formatter:off         http                 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)                 .and()                 .requestMatchers()                 .antMatchers("/**")                 .and()                 .authorizeRequests()                 .antMatchers(HttpMethod.OPTIONS, "/api/**").permitAll()                 .antMatchers(HttpMethod.GET, "/api/**").access("#oauth2.hasScope('read')")                 .antMatchers(HttpMethod.PATCH, "/api/**").access("#oauth2.hasScope('write')")                 .antMatchers(HttpMethod.POST, "/api/**").access("#oauth2.hasScope('write')")                 .antMatchers(HttpMethod.PUT, "/api/**").access("#oauth2.hasScope('write')")                 .antMatchers(HttpMethod.DELETE, "/api/**").access("#oauth2.hasScope('write')")                 .antMatchers("/admin/**").access("hasRole('ROLE_USER')");          // @formatter:on     }      @Override     public void configure(ResourceServerSecurityConfigurer resources) throws Exception {         System.out.println("Configuring ResourceServerSecurityConfigurer ");         resources.resourceId("dummy").tokenServices(defaultTokenServices());     }  } 

My test case is failing miserably saying:

{"error":"invalid_token","error_description":"Cannot convert access token to JSON"}

How do I get the Authentication object out of the JWT. How do I authenticate the client, with client credentials. How do I use @Secured annotation on my resource controllers.

What code is used on the resource server side to decode the token in order to extract client credentials and what code gets to user role verified?

Please help, as I already spent 2 days banging my head on this seemingly easy task.

Note: I receive the token from Auth server as: {access_token=b5d89a13-3c8b-4bda-b0f2-a6e9d7b7a285, token_type=bearer, refresh_token=43777224-b6f2-44d7-bf36-4e1934d32cbb, expires_in=43199, scope=read write trust, authorities=[{authority=ROLE_USER}, {authority=ROLE_ADMIN}]}

Please explain the concepts and point out if anything is missing from my configuration. I need to know the best practices in configuring my resource and auth server please.

1 Answers

Answers 1

In the following I'm referring to this Baeldung tutorial that I already implemented successfully: http://www.baeldung.com/spring-security-oauth-jwt

First at all: The CustomTokenEnhancer is used on the AuthorizationServer side to enhance a created token with additional custom information. You should use the so called DefaultAccessTokenConverter on the ResourceServer side to extract these extra claims.

You can @Autowire the CustomAccessTokenConverter into your ResourceServerConfiguration class and then set it to your JwtTokenStore() configuration.

ResourceServerConfiguration:

@Autowired private CustomAccessTokenConverter yourCustomAccessTokenConverter;  @Bean public TokenStore tokenStore() {     return new JwtTokenStore(accessTokenConverter()); }  @Bean public JwtAccessTokenConverter accessTokenConverter() {     JwtAccessTokenConverter converter = new JwtAccessTokenConverter();     converter.setAccessTokenConverter(yourCustomAccessTokenConverter);     converter.setSigningKey(yourSigningKey);     return converter; } 

The CustomAccessTokenConverter can be configured, so that the custom claims get extracted here.

CustomAccessTokenConverter:

@Component public class CustomAccessTokenConverter extends DefaultAccessTokenConverter {      @Override     public OAuth2Authentication extractAuthentication(Map<String, ?> claims) {         OAuth2Authentication authentication = super.extractAuthentication(claims);         authentication.setDetails(claims);         return authentication;     }  } 

(see: https://github.com/Baeldung/spring-security-oauth/blob/master/oauth-resource-server-1/src/main/java/org/baeldung/config/CustomAccessTokenConverter.java )

Read More

Monday, June 18, 2018

Basic Auth does not work with multiple configuration

Leave a Comment

I'm trying to setup basic authentication for one specific path in an application with already some configured security policies. I'm using Spring Boot 2.0

Here's my configuration:

@Configuration @EnableWebSecurity class SecurityConfig {      @Configuration     @Order(1)     inner class TokenWebSecurityConfig : WebSecurityConfigurerAdapter() {          override fun configure(http: HttpSecurity) {             http.antMatcher("/token")                 .authorizeRequests()                 .antMatchers(HttpMethod.POST, "/token").permitAll()                 .anyRequest().denyAll()                 .and()                 .sessionManagement()                 .sessionCreationPolicy(SessionCreationPolicy.STATELESS)                 .and()                 .csrf().disable()         }     }      @Configuration     @Order(2)     inner class SignUpWebSecurityConfig(private val signUpBasicAuthConfig: SignUpBasicAuthConfig) :         WebSecurityConfigurerAdapter() {          override fun configure(http: HttpSecurity) {             http                 .antMatcher("/signup")                 .csrf()                 .disable()                 .authorizeRequests()                 .anyRequest().authenticated()                 .and()                 .httpBasic()                 .and()                 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)         }          override fun configure(auth: AuthenticationManagerBuilder) {             auth.inMemoryAuthentication()                 .withUser(signUpBasicAuthConfig.username)                 .password(signUpBasicAuthConfig.password)                 .roles("USER")         }     }      @Configuration     @Order(3)     inner class ApiWebSecurityConfig(private val service: TokenAuthenticationUserDetailsService) :         WebSecurityConfigurerAdapter() {          override fun configure(http: HttpSecurity) {             http                 .antMatcher("/api/**")                 .authorizeRequests()                 .anyRequest().authenticated()                 .and()                 .addFilterBefore(authFilter(), RequestHeaderAuthenticationFilter::class.java)                 .authenticationProvider(preAuthProvider())                 .sessionManagement()                 .sessionCreationPolicy(SessionCreationPolicy.STATELESS)                 .and()                 .csrf().disable()         }          @Bean         fun authFilter(): TokenAuthenticationFilter = TokenAuthenticationFilter()          @Bean         fun preAuthProvider(): AuthenticationProvider =             PreAuthenticatedAuthenticationProvider().apply { setPreAuthenticatedUserDetailsService(service) }     }      @Configuration     @Order(4)     inner class HealthWebSecurityConfig : WebSecurityConfigurerAdapter() {          override fun configure(http: HttpSecurity) {             http.authorizeRequests().antMatchers(HttpMethod.GET, "/health").permitAll()         }     }      @Configuration     class AuthenticationManagerProvider : WebSecurityConfigurerAdapter() {          @Bean         override fun authenticationManagerBean(): AuthenticationManager = super.authenticationManagerBean()     } } 

But the @Order(2) configuration always fails. Logs:

14:23:00.318 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/signup'; against '/token' 14:23:00.319 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/signup'; against '/signup' 14:23:00.320 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - /signup at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' 14:23:00.321 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - /signup at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' 14:23:00.322 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - /signup at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter' 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - /signup at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter' 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', GET] 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /signup' doesn't match 'GET /logout 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', POST] 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/signup'; against '/logout' 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', PUT] 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /signup' doesn't match 'PUT /logout 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', DELETE] 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /signup' doesn't match 'DELETE /logout 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - No matches found 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - /signup at position 5 of 11 in additional filter chain; firing Filter: 'BasicAuthenticationFilter' 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.w.BasicAuthenticationFilter - Basic Authentication Authorization header found for user 'username' 14:23:00.327 [http-nio-8080-exec-1] DEBUG o.s.s.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider 14:23:00.473 [http-nio-8080-exec-1] WARN  o.s.s.c.bcrypt.BCryptPasswordEncoder - Encoded password does not look like BCrypt 14:23:00.473 [http-nio-8080-exec-1] DEBUG o.s.s.a.d.DaoAuthenticationProvider - Authentication failed: password does not match stored value 14:23:00.474 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.w.BasicAuthenticationFilter - Authentication request for failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials 14:23:00.474 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.DelegatingAuthenticationEntryPoint - Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest] 14:23:00.474 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.DelegatingAuthenticationEntryPoint - No match found. Using default entry point org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint@44d29834 

It seems to me that for some reason the in memory AuthenticationManager is not used. Any idea?

2 Answers

Answers 1

I had a similair issue, since spring 2.0 the passwords need to have a prefix indicating how they are encrypted. For example a bcrypt password would look like this:

{bcrypt}2187jbfsafsd 

If this does not work immediately I also made a few more edits to make it work in my application:

I first created a Passwordencoder bean and pasted it in the securityconfig:

@Bean public static PasswordEncoder passwordEncoder() {    return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } 

Then I modified the DaoAuthenticationProvider (also in the securityconfig):

@Bean public DaoAuthenticationProvider authenticationProvider() {     final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();     authProvider.setUserDetailsService(userDetailsService);     authProvider.setPasswordEncoder(passwordEncoder);     return authProvider; } 

And lastly I autowired the encoder into the securityconfig:

@Autowired private PasswordEncoder passwordEncoder; 

I hope this helps you

Answers 2

I think your problem relates to having multiple WebSecurityConfigurerAdapter instances and expect them all to be used, as far as I know Spring will only use one. This has been discussed further here: Using multiple WebSecurityConfigurerAdapter in spring boot.

My preferred solution is to define a special interface

public interface ServiceWebSecurityConfigurer {     void configure(HttpSecurity http) throws Exception; } 

Then have just one ConfigurerAdapter:

public class MyConfigurerAdapter extends WebSecurityConfigurerAdapter {      @Autowired(required = false)     List<ServiceWebSecurityConfigurer> securityConfigurers;      public void configure(HttpSecurity http) throws Exception {         http.authorizeRequests(). // whatever          for (ServiceWebSecurityConfigurer serviceSecConfig: securityConfigurers)             serviceSecConfig.configure(http);          http.authorizeRequests(). // whatever     } } 
Read More

Tuesday, June 12, 2018

I am getting exception-org.apache.catalina.core.ApplicationDispatcher.invoke threw exception java.lang.IllegalStateException

Leave a Comment

I am getting the following error in my server log,

08-May-2018 12:00:07.460 SEVERE [https-jsse-nio-8443-exec-1] org.apache.catalina.core.ApplicationDispatcher.invoke Servlet.service() for servlet [jsp] threw exception java.lang.IllegalStateException: getOutputStream() has already been called for this response     at org.apache.catalina.connector.Response.getWriter(Response.java:624)     at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:211)     at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:109)     at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:109)     at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:109)     at org.springframework.security.web.util.OnCommittedResponseWrapper.getWriter(OnCommittedResponseWrapper.java:149)     at org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:118)     at org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:111)     at org.apache.jasper.runtime.PageContextImpl.release(PageContextImpl.java:176)     at org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:120)     at org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:75)     at org.apache.jsp.WEB_002dINF.views.uncaughtException_html._jspService(uncaughtException_html.java:209)       08-May-2018 12:00:07.468 SEVERE [https-jsse-nio-8443-exec-1] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [xyz] in context with path [] threw exception java.lang.IllegalStateException: getOutputStream() has already been called for this response] with root cause java.lang.IllegalStateException: getOutputStream() has already been called for this response          at org.apache.catalina.connector.Response.getWriter(Response.java:624)     at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:211)     at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:109)     at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:109)     at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:109)     at org.springframework.security.web.util.OnCommittedResponseWrapper.getWriter(OnCommittedResponseWrapper.java:149)     at org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:118)     at org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:111)     at org.apache.jasper.runtime.PageContextImpl.release(PageContextImpl.java:176)     at org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:120)     at org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:75)     at org.apache.jsp.WEB_002dINF.views.uncaughtException_html._jspService(uncaughtException_html.java:209)     at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)     at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)     at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443)     at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:386)     at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:330)     at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) 

I do not know which method is throwing this error and what is the cause of this error. Actually this exception occured at the time when I deployed my application in tomcat and trying to get index page(Sometime first time, sometime second, sometime third the point is it comes no matter when it comes.), actually according to my configuration when ssl is enabled and when I deploy my application and trying to get welcome page session expired page is called and from now once I tried to get some other pages it throws the above exception. I am using spring 4 ,spring security, maven, hibernate 4. Can any one help me? Any Help will be appriciated.

web.xml

<web-app>     <display-name>Smart app Application</display-name>      <context-param>         <param-name>contextConfigLocation</param-name>         <param-value>/WEB-INF/spring-security.xml, /WEB-INF/app-servlet.xml         </param-value>     </context-param>      <listener>         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>     </listener>      <listener>         <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>     </listener>      <servlet>         <servlet-name>app</servlet-name>         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>         <load-on-startup>1</load-on-startup>     </servlet>      <servlet-mapping>         <servlet-name>app</servlet-name>         <url-pattern>/</url-pattern>     </servlet-mapping>      <servlet-mapping>         <servlet-name>jsp</servlet-name>         <url-pattern>/WEB-INF/views/*</url-pattern>     </servlet-mapping>      <security-constraint>         <web-resource-collection>             <web-resource-name>application</web-resource-name>             <url-pattern>/*</url-pattern>         </web-resource-collection>         <user-data-constraint>             <transport-guarantee>CONFIDENTIAL</transport-guarantee>         </user-data-constraint>     </security-constraint>      <filter>         <filter-name>encodingFilter</filter-name>         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>         <init-param>             <param-name>encoding</param-name>             <param-value>UTF-8</param-value>         </init-param>         <init-param>             <param-name>forceEncoding</param-name>             <param-value>true</param-value>         </init-param>     </filter>     <filter-mapping>         <filter-name>encodingFilter</filter-name>         <url-pattern>/*</url-pattern>     </filter-mapping>      <filter>         <filter-name>springSecurityFilterChain</filter-name>         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>     </filter>      <filter-mapping>         <filter-name>springSecurityFilterChain</filter-name>         <url-pattern>/*</url-pattern>     </filter-mapping>      <welcome-file-list>         <welcome-file>index</welcome-file>     </welcome-file-list>      <session-config>         <session-timeout>50</session-timeout>     </session-config> </web-app> 

0 Answers

Read More

Monday, June 11, 2018

Spring Security alters request URI

Leave a Comment

I integrated Spring Security into an existing Spring Boot project (Version: 1.5.3.RELEASE).

Before the integration, we've got our redirect informations from a request via getRequestURI in a preHandle method which extends HandlerInterceptorAdapater.

The Request URI is correctly pointing to their path (for example: /admin/login).

After the integration, the request URI is pointing to the complete path of the jsp.

In addition, we've registered a ContextUtil class to ConfigurableApplicationContext for further URI checkings. In this class, we fetch the request like this:

public HttpServletRequest getCurrentRequest() {     final ServletRequestAttributes servletRequestAttributes =      (ServletRequestAttributes)      RequestContextHolder.currentRequestAttributes();     return servletRequestAttributes.getRequest(); } 

but the URI is also to its "physical path" under /WEB-INF/

For example: GET Request is pointing to /WEB-INF/pages/admin/admin_login.jsp:

My WebSecurityConfig Class is :

@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter {     @Override     protected void configure(HttpSecurity http) throws Exception     {         //jeden Aufruf akzeptieren. Authorisierung und      Authentifizierung von Spring Security wird nicht genutzt     http.authorizeRequests().antMatchers("/").permitAll(); }      @Override     public void configure(WebSecurity web) throws Exception     {     web.ignoring().antMatchers("/resources/**", "/css/**", "/js/**",      "/img/**", "resources/*", "/WEB-INF/**").and().debug(true);     } } 

Relevant applicationContext.xml parts:

<mvc:default-servlet-handler/> <mvc:annotation-driven/> <mvc:resources mapping="/resources/**" location="classpath:/WEB-INF/resources/" />  <mvc:interceptors>     <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">         <property name="paramName" value="lang" />     </bean>     <bean class="de.abc.xyu.zzz.interceptor.RedirectInterceptor" /> </mvc:interceptors>  <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">     <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />     <property name="prefix" value="/WEB-INF/pages/" />     <property name="suffix" value=".jsp" />     <property name="redirectHttp10Compatible" value="false" /> </bean> 

Debug Log from Spring Security:

Request received for GET '/admin/login':

org.apache.catalina.connector.RequestFacade@70ad489

servletPath:/admin/login pathInfo:null headers: host: localhost:8081 connection: keep-alive cache-control: max-age=0 user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 upgrade-insecure-requests: 1 accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8 referer: http://localhost:8081/admin/login accept-encoding: gzip, deflate, br accept-language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7 cookie: JSESSIONID=AE07684D485DA698F1AA4DFE056D5B3A; JSESSIONID=0819B947A685FE3362F23E39CE999D3B

Security filter chain: [ WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter HeaderWriterFilter CsrfFilter
LogoutFilter RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter SessionManagementFilter
ExceptionTranslationFilter FilterSecurityInterceptor ]


[http-nio-8081-exec-1] INFO Spring Security Debugger -


Request received for GET '/WEB-INF/pages/admin/admin_login.jsp':

SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper@2eac9514]

servletPath:/WEB-INF/pages/admin/admin_login.jsp pathInfo:null headers: host: localhost:8081 connection: keep-alive cache-control: max-age=0 user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 upgrade-insecure-requests: 1 accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8 referer: http://localhost:8081/admin/login accept-encoding: gzip, deflate, br accept-language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7 cookie: JSESSIONID=AE07684D485DA698F1AA4DFE056D5B3A; JSESSIONID=0819B947A685FE3362F23E39CE999D3B

Security filter chain: [] empty (bypassed by security='none')

Why is the request pointing to its physical path under /WEB-INF/pages/login.jsp instead of its resolved path and how can we achieve it, that we get the "correct" URI?

1 Answers

Answers 1

Eventually this worked for me:

final ServletRequestAttributes servletRequestAttributes =      (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();  System.out.println("REQUEST URI: " +      servletRequestAttributes.getRequest()          .getAttribute("javax.servlet.forward.request_uri")); 

That gives the real request URI, not its "physical path" under /WEB-INF/.

Read More

Friday, March 2, 2018

Spring Security Initialization throwing UnsatisfiedDependencyException on mvcContentNegotiationManager

Leave a Comment

I am trying to implement Spring Security 5.0.0.RELEASE in an existing Spring MVC project. Note that it is entirely Annotation Based.

Following is the code for my WebAppInitializer :

package com.abc.webapp.core;  public class WebAppInitializer implements WebApplicationInitializer{     @Override     public void onStartup(ServletContext container) throws ServletException {         AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();         context.setConfigLocation("com.abc.webapp.config");         container.addListener(new ContextLoaderListener(context));         ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcherServlet",                 new DispatcherServlet(context));         dispatcher.setLoadOnStartup(1);         dispatcher.addMapping("/");     } } 

Following is the WebMVCConfig File -

package com.abc.webapp.config;  @EnableWebMvc @Configuration @ComponentScan(basePackages = { "com.abc.webapp.controller" }) public class AppContextWebConfig extends WebMvcConfigurerAdapter {      @Bean     public InternalResourceViewResolver resolver() {         InternalResourceViewResolver resolver = new InternalResourceViewResolver();         resolver.setViewClass(JstlView.class);         resolver.setPrefix("/WEB-INF/views/");         resolver.setSuffix(".jsp");         return resolver;     }      @Override     public void addResourceHandlers(ResourceHandlerRegistry registry) {         registry.addResourceHandler("/resources/css/**").addResourceLocations("/WEB-INF/css/");         registry.addResourceHandler("/resources/js/**").addResourceLocations("/WEB-INF/js/");     } } 

Now as per the Spring Security Docs I am trying to configure like following -

package com.abc.webapp.config;  @EnableWebSecurity @Configuration public class AppContextSecurityConfig extends WebSecurityConfigurerAdapter{      @Autowired     public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {         auth.inMemoryAuthentication().                 withUser(User.withDefaultPasswordEncoder()                         .username("user")                         .password("password")                         .roles("USER")                 );     }      @Override     protected void configure(HttpSecurity http) throws Exception {        // Code for Login URL and Logout URL      } } 

And

package com.abc.webapp.core;  public class SecurityWebAppInitializer extends AbstractSecurityWebApplicationInitializer{  } 

When I am trying to start the server I am getting the following stacktrace -

[ERROR][2018-01-30 01:40:13 ContextLoader:351] - Context initialization failed org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'appContextSecurityConfig': Unsatisfied dependency expressed through method 'setContentNegotationStrategy' parameter 0: Error creating bean with name 'mvcContentNegotiationManager' defined in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.accept.ContentNegotiationManager]: Factory method 'mvcContentNegotiationManager' threw exception; nested exception is java.lang.AbstractMethodError; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mvcContentNegotiationManager' defined in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.accept.ContentNegotiationManager]: Factory method 'mvcContentNegotiationManager' threw exception; nested exception is java.lang.AbstractMethodError         at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:651)         at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)         at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:350)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) 

I am quite sure I am not missing any steps mentioned in the Srping setup. I have googled several times but of no use. I am also tried removing the SecurityWebAppInitializer and manually adding the filter in the WebAppInitializer like the following way.

FilterRegistration.Dynamic  springSecurityFilterChain = container.addFilter("springSecurityFilterChain", DelegatingFilterProxy.class); springSecurityFilterChain.addMappingForUrlPatterns(null, false, "/*"); 

And I am still getting the exception during startup. Any clues or solutions are highly appreciated.

1 Answers

Answers 1

It might be due to an incompatibility of your spring-web-X.X.X.RELEASE.jar , Spring Data Commons jar. Please check spring jar versions using following command

mvn dependency:tree. 
Read More

Wednesday, February 28, 2018

Providing way to configure spring security?

Leave a Comment

Is it possible to configure spring security in a way that it reads configuration details from external file and configures accordingly.

(I am not taking about changing config at runtime. I am talking about reading from a file at the time of startup)

An example of my existing Sporing security config is:

@EnableWebSecurity @Configuration public class SecurityConfig {      @Bean                                                                  public UserDetailsService userDetailsService() throws Exception {         InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();         manager.createUser(User.withUsername("user").password("userPass").roles("USER").build());         manager.createUser(User.withUsername("admin").password("adminPass").roles("ADMIN").build());         return manager;     }       @Configuration     @Order(1)                                                             public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {          @Override                public void configure(AuthenticationManagerBuilder auth)            throws Exception {                         auth.inMemoryAuthentication().withUser("user").password("user").roles("USER");             auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");         }          protected void configure(HttpSecurity http) throws Exception {             http                 .antMatcher("/api/v1/**")                                                .authorizeRequests()                 .antMatchers("/api/v1/**").authenticated()                     .and()                 .httpBasic();         }     }      @Configuration     @Order(2)     public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {          @Override                public void configure(AuthenticationManagerBuilder auth)            throws Exception {              auth.inMemoryAuthentication().withUser("user1").password("user").roles("USER");             auth.inMemoryAuthentication().withUser("admin1").password("admin").roles("ADMIN");         }          @Override         protected void configure(HttpSecurity http) throws Exception {             http                 .antMatcher("/api/test/**")                 .authorizeRequests()                 .antMatchers("/api/test/**").authenticated()                     .and()                 .formLogin();         }     } } 

As you can see, I am using multiple configurations (have a look at Order() annotation). What I want to be able to do is decide at the time of startup number and types of configuration. Example first client may want to have 2 configs e.g.LdapConfig and SamlConfig. Other may want LdapConfig and SqlConfig and third may want 4-5 configs. Is it possible to do that?

NOTE: I am not using Spring Boot

EDIT

Summary of why I wnat it this way:

By customer I mean the company that will be buying my product. And by users I mean the actual end users of the company that bought my product. So I shipped the product to 3 companies. First will configure it to have ldap auth flow and google-oauth2 auth flow. Users of this first company will be seeing a login page with these 2 options. Company 2 now might have a ldap auth flow and saml auth flow and users of that company will be seeing those 2 options. And the company is selecting the available options before startup.

5 Answers

Answers 1

You could load properties, e.g. DB credentials, before creating your WebApplicationContext. Look at the following example:

public class WebAppInitializer implements WebApplicationInitializer {  @Override public void onStartup(ServletContext servletContext) throws ServletException {     // Tell the EnvironmentManager to load the properties. The path to the config      // file is set by Tomcat's home variable. If you change the container you might      // need to change this, too.     EnvironmentParamManager.initialize(System.getProperty("catalina.home"));      // now create the Spring Context     AnnotationConfigWebApplicationContext rootContext =          new AnnotationConfigWebApplicationContext();     rootContext.register(RootConfig.class);     rootContext.setServletContext(servletContext);     SpringApplicationContextProvider.configure(rootContext);     // ... other config } 

The EnvironmentParamManager could look like this. I've decided to make it static so that the properties are accessible from everywhere even in non-Spring parts of the application.

public class EnvironmentParamManager {    private static Properties properties = new Properties();    public static void initialize(String pathToConfigFile) {     BufferedInputStream stream;     try {         stream = new BufferedInputStream(new FileInputStream(            pathToConfigFile + "myconfig.props"));         properties.load(stream);                     stream.close();     } catch (Throwable e) {         throw new Error("Cannot read environment settings from file " + pathToConfigFile);     }   }    public static String getMongoDBHostname() {     return properties.getProperty("mongodb.username");   }  } 

When using JavaConfig, you can access your config properties at the Bean creation phase easily like this

@Configuration public class CoreConfig {  @Bean public MongoDbFactory mongoDbFactory() throws Exception {   ...   ServerAddress address = new       ServerAddress(EnvironmentParamManager.getMongoDBHost(),                     EnvironmentParamManager.getMongoDBPort());   ... } 

Of course, you are free to connect to any other services like LDAP etc. in just the same way as you load the local properties file before the Spring Context is bootstrapped. Hope that helps.

Answers 2

Have you tried this:

@EnableWebSecurity @Configuration public class SecurityConfig {      @Bean     public UserDetailsService userDetailsService() throws Exception {         InMemoryUserDetailsManager manager = new MemoryUserDetailsManager();        manager.createUser(User.withUsername("user").password("userPass").roles("USER").build());     manager.createUser(User.withUsername("admin").password("adminPass").roles("ADMIN").build());     return manager; }   @Configuration @Profile({"profile1", "profile2"}) @Order(1) public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {      @Override     public void configure(AuthenticationManagerBuilder auth)             throws Exception {         auth.inMemoryAuthentication().withUser("user").password("user").roles("USER");         auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");     }      protected void configure(HttpSecurity http) throws Exception {         http                 .antMatcher("/api/v1/**")                 .authorizeRequests()                 .antMatchers("/api/v1/**").authenticated()                 .and()                 .httpBasic();     } }  @Configuration @Profile("profile1") @Order(2) public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {      @Override     public void configure(AuthenticationManagerBuilder auth)             throws Exception {          auth.inMemoryAuthentication().withUser("user1").password("user").roles("USER");         auth.inMemoryAuthentication().withUser("admin1").password("admin").roles("ADMIN");     }      @Override     protected void configure(HttpSecurity http) throws Exception {         http                 .antMatcher("/api/test/**")                 .authorizeRequests()                 .antMatchers("/api/test/**").authenticated()                 .and()                 .formLogin();         }     } } 

So with spring.profiles.active=profile1, both configurations are loaded, with spring.profiles.active=profile2, only the first configuration is loaded. Of course, you can use more than 2 profiles, and you can also activate more than one profile at startup (also comma separated). You just need to divide your configurations and profiles in a way that fits your requirements.

Answers 3

Selective loading of components can be achived with Springs @Conditional annotation.

The configs would look like this:

@Configuration(value = "some.security.config") @Conditional(value = LoadSecurityConfigCondition.class) public class SomeSecurityConfig {   // some code }  @Configuration(value = "other.security.config") @Conditional(value = LoadSecurityConfigCondition.class) public class OtherSecurityConfig {   // other code } 

Then, the LoadSecurityConfigCondition.class decides if the components are loaded:

@Component public class LoadSecurityConfigCondition implements Condition {    @Override   public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {     boolean enabled = false;      if (metadata.isAnnotated(Configuration.class.getName())) {       final String name = (String) metadata.getAnnotationAttributes(Configuration.class.getName()).get("value");       if (StringUtils.isNotBlank(name)) {         /* Here you may load your config file and           * retrieve the information on wether to load           * the config identified by its name.          */         enabled = ...;       }     }     return enabled;   } } 

In this eample, the config entries can now be created with the @Configuration name, postfixed with .enabled to clarify its purpose:

some.security.config.enabled=true other.security.config.enabled=false 

Answers 4

In order to manage multiple authentication providers you can use AuthenticationManagerBuilder, in this class you will add all the authentication providers you want to use.

@EnableWebSecurity public class MultipleAuthProvidersSecurityConfig    extends WebSecurityConfigurerAdapter {     @Autowired     CustomAuthenticationProvider customAuthProvider;      @Override     public void configure(AuthenticationManagerBuilder auth)        throws Exception {          auth.authenticationProvider(customAuthProvider);         auth.inMemoryAuthentication()             .withUser("memuser")             .password("pass")             .roles("USER");     }      @Override     protected void configure(HttpSecurity http) throws Exception {         http.httpBasic()             .and()             .authorizeRequests()             .antMatchers("/api/**")             .authenticated();     } } 

Answers 5

Spring Security gives you all of the tools almost out-of-the-box when securing against Java Server Pages: redirection to the login page, redirection to the right controller after user has authenticated, JSP taglibs, etc.

The fact is, there are a lot of modern web applications that rely on accessing back-end web services while implementing a rich and responsive user experience on the front-end, such as AngularJS-based web applications. And things can get a little trickier when authentication is done using web services: no redirection after login, the returned HTTP status codes are not necessarily the right ones… the web services’ client inherits a lot of responsibilities that were previously handled by the back-end.

Read More

Friday, February 9, 2018

SAML Http Request Intercept with Spring Boot

Leave a Comment

In reference to this SO question Add request parameter to SAML request using Spring Security SAML

I am wanting to replace the default HTTPRedirectDeflateBinding bean with my own that has a custom HTTPRedirectDeflateEncoder to add query params to my SAML request.

I'm trying to achieve this with the Spring Boot @Bean auto-configuration annotation and being new to the Java environment I can't seem to get it working right. I can see that my bean is registering on startup but the outbound HTTP request is not being intercepted by it and it appears the original redirectBinding still is.

Here is my bean I added into my Configuration class:

@Bean(name="redirectBinding") @Primary public HTTPRedirectDeflateBinding HTTPRedirectDeflateBinding() {     return new HTTPRedirectDeflateBinding(null, new My_SAML_HttpRedirectDeflateEncoder()); } 

Here is my encoder I'm trying to pass into the redirect binding

public class My_SAML_HttpRedirectDeflateEncoder extends HTTPRedirectDeflateEncoder{  @Override protected String buildRedirectURL(SAMLMessageContext messagesContext, String endpointURL, String message)         throws MessageEncodingException {     URLBuilder urlBuilder = new URLBuilder(endpointURL);     List<Pair<String, String>> queryParams = urlBuilder.getQueryParams();      if (messagesContext.getOutboundSAMLMessage() instanceof RequestAbstractType) {         queryParams.add(new Pair<String, String>("service", "myService"));         queryParams.add(new Pair<String, String>("serviceType", "dev"));     }      return urlBuilder.buildURL(); } 

}

I also attempted the solution proposed from this SO response spring boot adding http request interceptors Similar results, my HandlerInterceptor bean was registered but nothing is being intercepted. I feel like I'm missing a small detail. Any help would be appreciated.

1 Answers

Answers 1

You can redeclare the SAMLProcessor bean - which is used by SAMLProcessingFilter - and add your own binding bean in its bindings list. This is an example, I used in my project.

@Bean public SAMLProcessorImpl processor() {     Collection<SAMLBinding> bindings = new ArrayList<>();     bindings.add(httpRedirectDeflateBinding());     bindings.add(httpPostBinding());     bindings.add(artifactBinding(parserPool(), velocityEngine()));     bindings.add(httpSOAP11Binding());     bindings.add(httpPAOS11Binding());      return new SAMLProcessorImpl(bindings); } 

Hope it works for you.

Read More

Monday, January 29, 2018

LDap GSSContext null srcName with spring security

Leave a Comment

We try to make Windows authentication using spring security.

When we saw that we cannot authenticate our domain user with our keytab file created for our local pc, we checked our service user and see that it's password is valid. Then we checked whether we can reach from local to AD-domain. No request reached from our local as we controlled with network monitoring tool on AD-domain server machine. We also checked that outgoing traffic from our client with the command below;

netstat -oan 1 | find /I "[IP_ADDRESS_OF_AD_DOMAIN]" 

We could reach to that IP from our local, tested with telnet.

Our application.properties is like below;

app.ad-domain= example.com app.ad-server= ldap://adds.example.com.tr/ app.service-principal= HTTP/local_pc.example.com.tr@EXAMPLE.COM.TR app.keytab-location= local_pc.keytab app.ldap-search-base= OU=All Users,DC=example,DC=com app.ldap-search-filter= "(| (userPrincipalName={0}) (sAMAccountName={0}))" 

As a result we cannot get srcName of GSSContext. This gssName variable equals to null. Related SunJaasKerberosTicketValidator code block is as below;

@Override public KerberosTicketValidation run() throws Exception {     byte[] responseToken = new byte[0];     GSSName gssName = null;     GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);     boolean first = true;     while (!context.isEstablished()) {         if (first) {             kerberosTicket = tweakJdkRegression(kerberosTicket);         }         responseToken = context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);         gssName = context.getSrcName();         if (gssName == null) {             throw new BadCredentialsException("GSSContext name of the context initiator is null");         }         first = false;     }     if (!holdOnToGSSContext) {         context.dispose();     }     return new KerberosTicketValidation(gssName.toString(), servicePrincipal, responseToken, context); } 

As we searched this GSSContext with null SrcName error, in general suggested solutions are related to keytab file . But in our problem, we cannot even reach AD server as we mentioned in the beginning.

related link: GSSContext with null SrcName

Is there any other suggestion?

Thanks...

0 Answers

Read More

Wednesday, January 24, 2018

Spring session replication with mongodb not working as expected

Leave a Comment

I am replicating session using mongodb

below is the configuration I am using

@Configuration @EnableMongoHttpSession public class MongoSessionReplication {  @Bean public AbstractMongoSessionConverter mongoSessionConverter() {     List<Module> securityModules = SecurityJackson2Modules.getModules(getClass().getClassLoader());     return new JacksonMongoSessionConverter(securityModules); } @Bean public MongoTemplate mongoTemplate(@Qualifier("replicaSet") Datastore replicaSet){    MongoTemplate mongoTemplate = new MongoTemplate(replicaSet.getMongo(),replicaSet.getDB().getName());    return mongoTemplate; }  } 

Now everything is working fine except the Principal object that spring security creates after loggin in. I have custom implementation of UserDetails

public class PortalUser extends User {      private String primaryEmailId;      private String redirectUrl;      public PortalUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {         super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);     }     public PortalUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {         super(username, password, true, true, true, true, authorities);     }      public String getPrimaryEmailId() {         return primaryEmailId;     }      public void setPrimaryEmailId(String primaryEmailId) {         this.primaryEmailId = primaryEmailId;     }      public String getRedirectUrl() {         return redirectUrl;     }      public void setRedirectUrl(String redirectUrl) {         this.redirectUrl = redirectUrl;     } } 

Below is UserDetailsService

@Service public class PortalUserDetailService implements UserDetailsService {      @Autowired     private SSOServiceAPI ssoServiceAPI;      @Autowired     private UserProfileService userProfileService;      @Override     public UserDetails loadUserByUsername(String hexId) throws UsernameNotFoundException {         UserProfile userProfile = userProfileService.getUserProfileByUserId(hexId);         List<GrantedAuthority> grantedAuthority = new ArrayList<GrantedAuthority>();         if(userProfile!=null) {             grantedAuthority.add(new SimpleGrantedAuthority(userProfile.getSsmRoles().name()));         } else {             grantedAuthority.add(new SimpleGrantedAuthority("USER"));         }         SSOUsers ssoUser = ssoServiceAPI.findSSOUser(hexId, false);         PortalUser portalUser = new PortalUser(hexId, hexId, true, true, true, true, grantedAuthority);         portalUser.setPrimaryEmailId(ssoUser.getPrimaryUserId());         return portalUser;      } } 

Controller

public String getAllProducts(@RequestParam(value = "callback", required = true) String callback, Principal principal, HttpServletRequest request) {      String hexId = principal.getName();     String primaryEmailId = ((PortalUser) ((UsernamePasswordAuthenticationToken) principal).getPrincipal()).getPrimaryEmailId(); //----->> this line fails  } 

Above highlighted typecasting failed as it returns instance of UserDetails instead of my custom PortalUser. But this isn't a case when I disable spring-session replication..

1 Answers

Answers 1

You need to extend Spring's Security UserDetails, not User. update PortUser to the below:

import org.springframework.security.core.userdetails.UserDetails;  public class PortalUser  extends UserDetails,Serializable { 
Read More

Monday, December 4, 2017

Spring oauth: Why resource server is authorising instead of authorisation server

Leave a Comment

What i found while using spring oauth framework is resource server making check_token?token=T_O_K_E_N request to authorisation server, and authorisation server is just returning CheckTokenEndPoint map with authorities something like below.

{     "exp": 1511471427,     "user_name": "idvelu",     "authorities": [         "FUNCTION_GET_USERS",         "FUNCTION_AUTHORITY_1",         "FUNCTION_AUTHORITY_2",         "FUNCTION_AUTHORITY_3",         "FUNCTION_AUTHORITY_4",         "FUNCTION_AUTHORITY_5",         "FUNCTION_AUTHORITY_6",         "FUNCTION_AUTHORITY_7",     ],     "client_id": "c1",     "scope": [         "read",         "write"     ] } 

Just visualise this with oauth service and resource service is running in two different machines/jvm.

I think now resource server has to authorise the request against configured valid authorities in ResourceServerConfiguration::configure(HttpSecurity) with the authorities from the authorisation server.

@Override public void configure(HttpSecurity http) throws Exception {  http.anonymous().disable().requestMatchers().antMatchers("/**").and().authorizeRequests()          .antMatchers(HttpMethod.GET, "/myproject/users").hasAnyAuthority("FUNCTION_GET_USERS")         .antMatchers(HttpMethod.POST, "/myproject/users").hasAnyAuthority("FUNCTION_POST_NEW_USER")         .anyRequest().denyAll()         .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler()); } 

In this case authorisation server may return all the hundreds of user authorities to resource server. Instead why not authorisation server itself can take the few of the permission required for authorisation as query params
check_token?token=T_O_K_E_N&authorities=FUNCTION_GET_USERS,FUNCTION_AUTHORITY_2,..
from the resource server and validate it against the user's functions through DB?

And finally my problem is; i have different services like java, node.js, NGINX... All these have to verify its authentication and authorization against one spring Authorisation server. Because of the above stated problem all my service has to implement the authorisation (resource server) part. Means comparing all the authorities of user against the API acess authorities.
Java side this comparison is fine with spring resource server implementation. But all other non-java (resource) services needs authorisation/resourceServer implementation. Instead if my spring authorisation server accepts the authorities and validates then my problem is solved as single point of authorisation/comparison implementations. I just need to pass it as part of check_token.

How to implement this new check_token endpoint along with accepting the authorities?

1 Answers

Answers 1

The authorization server is responsible for authenticating the user/client (depending on the oauth type you use). When this is done, a token is given.

When a user/client presents themselves to the resource server, wanting to consume a service, they must provide the token. Now the resource server has a token and needs to validate that this token was generated by the authorization server. There are two options:

  1. The token itself does not contain any information. The resource server calls the authorization server and asks if the token is valid. The authorization server will respond that the token is valid and gives some additional information (user/client, roles/scopes, etc).
  2. The token does contain the necessary information (JWT tokes for example). This enables the resource server to extract the needed info without contacting the authorization server. In this case, the authorization server has signed the token and the resource server can validate the signature to be sure that it was the autorization server that has issued the token.

At the moment, you are using the first scenario. Every resource server you write, must verify tokens and extract additional info. How the verification is done depends on the authorization server.

Part2:

Your question is not clear to me. I presume you are using the OAuth2 authorization code scheme with non JWT tokens?

In that case, you have an authorization server that is only responsible for authentication, a resource server that is exposing some services and a client that consumes the resource server.

If i'm not mistaking, you have different resource servers (api's)?

You did not share your authorization server configurations, but normaly you use @EnableAuthorizationServer. This will create an endpoint /oauth/check_token. By default this endpoint is not accassible. You need to do something like this:

@Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {    oauthServer.checkTokenAccess("permitAll()"); // authenticated is better } 

You can use this endpoint to validate tokens.

All this is described in the oauth2 developer guide.

Part3:

On the authorization server, you can create an endpoint like this :

@RequestMapping("/user") public Principal user(Principal principal) {      if(principal instanceof OAuth2Authentication) {         return (OAuth2Authentication) principal;     } else {         return principal;     } } 

You can modify it to your needs.

Read More

Wednesday, November 29, 2017

How to prevent HTTP session flood attack

Leave a Comment

Flood Attack: In short, a hacker can keep hitting the server (without cookie) to force Java container to keep creating new session.

I am using Spring Security to manage session. I realize jsessionid keep being created before login, this is not what I want.

So I did:

1) in Spring security config:

sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER) 

2) disable session creation in jsp. Because I am using apache tile, due to it is using dynamic include, so I have to disable session creation in all the jsp fragment. This is very tedious.

<%@page session="false"%> 

First glance, it is fine, but there is a scenario I still got the session created.

Let's say before login, I visit a url that only can be visited after authenticated, Spring will redirect me to login page.

Before I am redirected, the response already instruct to set a new cookie, a session already created.

My Question:

1) Is session flood attack a serious issue? Should I really take care of it?

2) Is there any better way to handle this issue? Any best practise?

3) What happen to my code? It should work actually, I suspect the cookie is created by Spring, although I already set it to SessionCreationPolicy.NEVER. I can't set it to Stateless, I still need the session after login.

I am more concerned session attack compare to DDOS actually, I have also set .maximumSessions(1) in Spring to prevent multiple login. But above issue happen before login. Please help. Thanks.

1 Answers

Answers 1

Your point looks valid, it probably can be a serious issue if not handled. I found there is already an open issue on this topic. But there is a work around available to control this behavior.

public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {         public HttpSessionRequestCache getHttpSessionRequestCache() {             HttpSessionRequestCache httpSessionRequestCache = new HttpSessionRequestCache();             httpSessionRequestCache.setCreateSessionAllowed(false);             return httpSessionRequestCache;         }          @Override         protected void configure(final HttpSecurity http) throws Exception {             http.requestCache().requestCache(getHttpSessionRequestCache()); } 

Refer following links for more details.

https://github.com/spring-projects/spring-security/issues/4242

How to stop Spring Security from creating a new session?

Read More

Wednesday, November 15, 2017

Spring Security - multiple configurations - add LogoutHandler

Leave a Comment

I have a spring-boot application using spring-security. The security configuration is split into multiple instances of WebSecurityConfigurerAdapter.

I have one where I configure logout in general:

@Override protected void configure(HttpSecurity http) throws Exception {      // configure logout     http             .logout()             .logoutUrl("/logout")             .invalidateHttpSession(true)             .addLogoutHandler((request, response, authentication) -> {                 System.out.println("logged out 1!");             })             .permitAll();      // ... more security configuration, e.g. login, CSRF, rememberme } 

And there is another WebSecurityConfigurerAdapter, where I want to do almost nothing, except adding another LogoutHandler:

@Override protected void configure(HttpSecurity http) throws Exception {      // configure logout     http             .logout()             .logoutUrl("/logout")             .addLogoutHandler((request, response, authentication) -> {                 System.out.println("logged out 2!");             }); } 

Both configure() methods are called. However, if I do log out, only the first LogoutHandler is called. Changing the @Order of both configurations does not change the result.

What is missing in my configuration?

3 Answers

Answers 1

When you create several security configurations Spring Boot will create a separate SecurityFilterChain for each of them. See WebSecurity:

@Override protected Filter performBuild() throws Exception {     // ...     for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {         securityFilterChains.add(securityFilterChainBuilder.build());     }     // ... } 

When application gets logout request FilterChainProxy will return only one SecurityFilterChain:

private List<Filter> getFilters(HttpServletRequest request) {     for (SecurityFilterChain chain : filterChains) {         // Only the first chain that matches logout request will be used:         if (chain.matches(request)) {             return chain.getFilters();         }     }      return null; } 

If you really need modular security configuration I would suggest to create a separate security configuration for logout and other realms. You can define logout handlers as beans (using @Bean annotation) in different configuration classes and collect these handlers in logout configuration:

WebSecurityLogoutConfiguration.java

@Configuration @Order(99) public class WebSecurityLogoutConfiguration extends WebSecurityConfigurerAdapter {      // ALL YOUR LOGOUT HANDLERS WILL BE IN THIS LIST     @Autowired     private List<LogoutHandler> logoutHandlers;      @Override     protected void configure(HttpSecurity http) throws Exception {         // configure only logout         http                 .logout()                 .logoutUrl("/logout")                 .invalidateHttpSession(true)                 // USE CompositeLogoutHandler                 .addLogoutHandler(new CompositeLogoutHandler(logoutHandlers));         http.csrf().disable(); // for demo purposes     } } 

WebSecurity1Configuration.java

@Configuration @Order(101) public class WebSecurity1Configuration extends WebSecurityConfigurerAdapter {      @Override     protected void configure(HttpSecurity http) throws Exception {         // ... more security configuration, e.g. login, CSRF, rememberme         http.authorizeRequests()                 .antMatchers("/secured/**")                 .authenticated();     }      // LOGOUT HANDLER 1     @Bean     public LogoutHandler logoutHandler1() {         return (request, response, authentication) -> {             System.out.println("logged out 1!");         };     } } 

WebSecurity2Configuration.java

@Configuration @Order(102) public class WebSecurity2Configuration extends WebSecurityConfigurerAdapter {      @Override     protected void configure(HttpSecurity http) throws Exception {         http.authorizeRequests()                 .antMatchers("/api/**")                 .permitAll();     }      // LOGOUT HANDLER 2     @Bean     public LogoutHandler logoutHandler2() {         return (request, response, authentication) -> {             System.out.println("logged out 2!");         };     } } 

Answers 2

You should be solving this problem with the CompositeLogoutHandler on your single /logout operation endpoint.

You can still keep two WebSecurityConfigurerAdapter's as desired, but you'll be conglomerating the logout functionality for two LogoutHandlers into a single composite action:

new CompositeLogoutHandler(loggedOutHandler1, loggedOutHandler2); 

Answers 3

The keypoint is you should create separated instance of AuthenticationManger.

Here is an sample for multiples WebSecurityAdapter

Read More