Skip to content

Commit

Permalink
gh-34 Fix: Remove token from the TokenStore on logout
Browse files Browse the repository at this point in the history
- Add `TokenStore` (InMemoryTokenStore) as an explicit Spring bean
- Add Custom `LogoutSuccessHandler` (TokenStoreClearingLogoutSuccessHandler)
  • Loading branch information
ghillert authored and ilayaperumalg committed Sep 20, 2019
1 parent eeb5f5c commit d2daa99
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.PrincipalExtractor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
Expand All @@ -39,6 +40,7 @@
import org.springframework.cloud.common.security.support.OnOAuth2SecurityEnabled;
import org.springframework.cloud.common.security.support.SecurityConfigUtils;
import org.springframework.cloud.common.security.support.SecurityStateBean;
import org.springframework.cloud.common.security.support.TokenStoreClearingLogoutSuccessHandler;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
Expand Down Expand Up @@ -67,8 +69,10 @@
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
Expand Down Expand Up @@ -111,6 +115,9 @@ public class OAuthSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
protected ResourceServerProperties resourceServerProperties;

@Autowired
protected OAuth2ClientProperties oAuth2ClientProperties;

@Autowired
protected ApplicationEventPublisher applicationEventPublisher;

Expand Down Expand Up @@ -156,9 +163,10 @@ protected void configure(HttpSecurity http) throws Exception {
security = SecurityConfigUtils.configureSimpleSecurity(security, this.authorizationProperties);
security.anyRequest().denyAll();


http.httpBasic().and()
.logout()
.logoutSuccessUrl(dashboard("/logout-success-oauth.html"))
.logoutSuccessHandler(logoutSuccessHandler())
.and().csrf().disable()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(
Expand All @@ -168,6 +176,18 @@ protected void configure(HttpSecurity http) throws Exception {
this.securityStateBean.setAuthenticationEnabled(true);
}

@Bean
LogoutSuccessHandler logoutSuccessHandler() {
final TokenStoreClearingLogoutSuccessHandler logoutSuccessHandler = new TokenStoreClearingLogoutSuccessHandler(tokenStore(), oAuth2ClientProperties);
logoutSuccessHandler.setDefaultTargetUrl(dashboard("/logout-success-oauth.html"));
return logoutSuccessHandler;
}

@Bean
TokenStore tokenStore() {
return new InMemoryTokenStore();
}

@Bean
protected TokenValidatingUserInfoTokenServices resourceServerTokenServices() {

Expand All @@ -177,7 +197,7 @@ protected TokenValidatingUserInfoTokenServices resourceServerTokenServices() {
authorizationCodeResourceDetails.getClientId(),
authorizationCodeResourceDetails.getClientSecret());

tokenServices.setTokenStore(new InMemoryTokenStore());
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);

tokenServices.setRestTemplate(oAuth2RestTemplate());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.common.security.support;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.cloud.common.security.ManualOAuthAuthenticationDetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;

/**
* @author Gunnar Hillert
*/
public class TokenStoreClearingLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {

private static final Logger logger = LoggerFactory.getLogger(TokenStoreClearingLogoutSuccessHandler.class);

private TokenStore tokenStore;
private OAuth2ClientProperties oAuth2ClientProperties;

public TokenStoreClearingLogoutSuccessHandler(TokenStore tokenStore, OAuth2ClientProperties oAuth2ClientProperties) {
this.tokenStore=tokenStore;
this.oAuth2ClientProperties = oAuth2ClientProperties;
}

public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {

final int numberOfTokensBeforeRemoval = tokenStore.findTokensByClientId(oAuth2ClientProperties.getClientId()).size();
if (authentication instanceof OAuth2Authentication) {
final OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication;

if (oAuth2Authentication.getDetails() instanceof ManualOAuthAuthenticationDetails) {
ManualOAuthAuthenticationDetails authenticationDetails = (ManualOAuthAuthenticationDetails) oAuth2Authentication.getDetails();
this.tokenStore.removeAccessToken(authenticationDetails.getAccessToken());
}
else if (oAuth2Authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails authenticationDetails = (OAuth2AuthenticationDetails) oAuth2Authentication.getDetails();
this.tokenStore.removeAccessToken(this.tokenStore.readAccessToken(authenticationDetails.getTokenValue()));
}
}

final int numberOfTokensAfterRemoval = tokenStore.findTokensByClientId(oAuth2ClientProperties.getClientId()).size();
final int numberOfRemovedTokens = numberOfTokensBeforeRemoval - numberOfTokensAfterRemoval;

if (numberOfRemovedTokens > 0) {
logger.error("Number of removed tokens: {}. Total number of tokens in store: {}",
numberOfRemovedTokens, numberOfTokensAfterRemoval);
}

super.handle(request, response, authentication);
}

}

0 comments on commit d2daa99

Please sign in to comment.