How to set expire_in in OAUTH 2.0? - spring-security

I am using OAuth 2.0 with spring for token generation and I want to set expire_in manually so token can expire as per my criteria. Any one help me?
This is my response:
{
access_token: "c7a6cb95-1506-40e7-87d1-ddef0a239f64"
token_type: "bearer"
expires_in: 43199
scope: "read"
}

It can be set with a ClientBuilder obtained from a ClientDetailsServiceConfigurer.
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.secret("secret")
.authorizedGrantTypes("authorization_code", "refresh_token", "password")
.scopes("app")
.accessTokenValiditySeconds(30);
}
// ... additional configuration
}
or directly on DefaultTokenServices depending on your need.
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// optionally here you could just get endpoints.getConsumerTokenService()
// and cast to DefaultTokenServices and just set values needed
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(endpoints.getTokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
tokenServices.setAccessTokenValiditySeconds(60);
endpoints.tokenServices(tokenServices);
}
}

configure your oauth configuration changing your Bean TokenServices and setting accessTokenValiditySeconds property :
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="accessTokenValiditySeconds" value="1" />
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>

You can also configure the DefaultTokenServices in the application.yaml file.
security:
oauth2:
client:
clientId: client-id
clientSecret: client-secret
authorized-grant-types: authorization_code,refresh_token,password
scope: openid
access-token-validity-seconds: 30

Create a custom class of AuthorizationCodeAccessTokenProvider and override the parent
public method obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)
In the overridden method of your custom class, call upon the program logic of its parent class:
DefaultOAuth2AccessToken token = super.obtainAccessToken(details, request);
This will return an AccessToken.
Now, you just have to manipulate the expired value of that token directly, by providing a timestamp from the past
token.setExpiresIn(int timestamp)

Also was searching for this answer and tried proposed solution from DeezCashews. But it didn't work for me, because there is a part of code which firstly check if this value is set in in column access_token_validity table oauth_client_details and only then greps value from tokenServices. So if your "expires_in" is set in oauth_client_details table, then you need to change it there.
Code which checks validity property in db :
protected int getAccessTokenValiditySeconds(OAuth2Request clientAuth) {
if (clientDetailsService != null) {
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
Integer validity = client.getAccessTokenValiditySeconds();
if (validity != null) {
return validity;
}
}
return accessTokenValiditySeconds;
}

If you are using grails security oauth2 provider
you can only change grails-app/conf/spring/resources.groovy
import org.springframework.security.oauth2.provider.token.DefaultTokenServices
// Place your Spring DSL code here
beans = {
tokenServices(DefaultTokenServices){
accessTokenValiditySeconds = 600;
tokenStore = ref('tokenStore')
supportRefreshToken = true;
clientDetailsService = ref('clientDetailsService')
}
}

As such I don't think there is any policy to do that so. But there is one way which can lead to success.
Just use refresh_token API to make the current access_token invalid. :D
Simple is that.

public interface OAuth2AccessToken {
public static String BEARER_TYPE = "Bearer";
public static String OAUTH2_TYPE = "OAuth2";
/**
* The access token issued by the authorization server. This value is REQUIRED.
*/
public static String ACCESS_TOKEN = "access_token";
/**
* The type of the token issued as described in <a
* href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-7.1">Section 7.1</a>. Value is case insensitive.
* This value is REQUIRED.
*/
public static String TOKEN_TYPE = "token_type";
/**
* The lifetime in seconds of the access token. For example, the value "3600" denotes that the access token will
* expire in one hour from the time the response was generated. This value is OPTIONAL.
*/
public static String EXPIRES_IN = "expires_in";
/**
* The refresh token which can be used to obtain new access tokens using the same authorization grant as described
* in Section 6. This value is OPTIONAL.
*/
public static String REFRESH_TOKEN = "refresh_token";
/**
* The scope of the access token as described by <a
* href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.3">Section 3.3</a>
*/
public static String SCOPE = "scope";
/**
* The additionalInformation map is used by the token serializers to export any fields used by extensions of OAuth.
* #return a map from the field name in the serialized token to the value to be exported. The default serializers
* make use of Jackson's automatic JSON mapping for Java objects (for the Token Endpoint flows) or implicitly call
* .toString() on the "value" object (for the implicit flow) as part of the serialization process.
*/
Map<String, Object> getAdditionalInformation();
Set<String> getScope();
OAuth2RefreshToken getRefreshToken();
String getTokenType();
boolean isExpired();
Date getExpiration();
int getExpiresIn();
String getValue();
}

Related

How to set modify the access token request entity for Client Credentials grant when using Spring Security OAuth2 framrwork

I'm writing client for a 3rd party service that doesn't have the standard request format for getting an access token. The access token request body is a JSON with two attributes and the client_id and client_secret needs to be sent as a basic auth header. How do I build the custom request entity and headers converter to appropriately set these values in the access token request?
I have the client configuration with the client manager and responseclient.
public class RestClientConfig {
private final ClientRegistrationRepository clientRegistrationRepository;
private final OAuth2AuthorizedClientRepository authorizedClientRepository;
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient){
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials(configurer -> configurer.accessTokenResponseClient(accessTokenResponseClient))
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
#Bean
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient(){
OAuth2ClientCredentialsGrantRequestEntityConverter requestEntityConverter =
new OAuth2ClientCredentialsGrantRequestEntityConverter();
requestEntityConverter.setParametersConverter(null); --> this is where I'm stuck. Need to build a request entity converter bean to pass to this method
DefaultClientCredentialsTokenResponseClient accessTokenResponseClient =
new DefaultClientCredentialsTokenResponseClient();
accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);
return accessTokenResponseClient;
}
}

Spring Oauth2 Client, automatically refresh expired access_token

Let me explain my use case.
I need to have a spring boot oauth2 client application (not a resource server As we already have a separate resource server). Also I have following requirements:
For each out going request to resource server, we need to send id_token. (Done by customizing resttemplate).
For any request, no matter if it invokes resource server or not, If access token is expired my application must refresh it automatically (without any user intervention like any popup or redirection.).
If refresh_token is also expired, user must be logged out.
Questions:
For point 2 and 3, I have spent many hours reading documents and code and Stack Overflow but was not able to find the solution (or did not understand). So I decided to put all pieces together which I found on many blogs and documents, and come up with my solution. Below is my solution for point 2.
Can we please have a look to below code and suggest if there could be any problem with this approach?
How to solve point 3 I am thinking of extending solution for point 2 but not sure what code I need to write, can anyone guide me?
/**
*
* #author agam
*
*/
#Component
public class ExpiredTokenFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(ExpiredTokenFilter.class);
private Duration accessTokenExpiresSkew = Duration.ofMillis(1000);
private Clock clock = Clock.systemUTC();
#Autowired
private OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
#Autowired
CustomOidcUserService userService;
private DefaultRefreshTokenTokenResponseClient accessTokenResponseClient;
private JwtDecoderFactory<ClientRegistration> jwtDecoderFactory;
private static final String INVALID_ID_TOKEN_ERROR_CODE = "invalid_id_token";
public ExpiredTokenFilter() {
super();
this.accessTokenResponseClient = new DefaultRefreshTokenTokenResponseClient();
this.jwtDecoderFactory = new OidcIdTokenDecoderFactory();
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
log.debug("my custom filter called ");
/**
* check if authentication is done.
*/
if (null != SecurityContextHolder.getContext().getAuthentication()) {
OAuth2AuthenticationToken currentUser = (OAuth2AuthenticationToken) SecurityContextHolder.getContext()
.getAuthentication();
OAuth2AuthorizedClient authorizedClient = this.oAuth2AuthorizedClientService
.loadAuthorizedClient(currentUser.getAuthorizedClientRegistrationId(), currentUser.getName());
/**
* Check if token existing token is expired.
*/
if (isExpired(authorizedClient.getAccessToken())) {
/*
* do something to get new access token
*/
log.debug(
"=========================== Token Expired !! going to refresh ================================================");
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
/*
* Call Auth server token endpoint to refresh token.
*/
OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(
clientRegistration, authorizedClient.getAccessToken(), authorizedClient.getRefreshToken());
OAuth2AccessTokenResponse accessTokenResponse = this.accessTokenResponseClient
.getTokenResponse(refreshTokenGrantRequest);
/*
* Convert id_token to OidcToken.
*/
OidcIdToken idToken = createOidcToken(clientRegistration, accessTokenResponse);
/*
* Since I have already implemented a custom OidcUserService, reuse existing
* code to get new user.
*/
OidcUser oidcUser = this.userService.loadUser(new OidcUserRequest(clientRegistration,
accessTokenResponse.getAccessToken(), idToken, accessTokenResponse.getAdditionalParameters()));
log.debug(
"=========================== Token Refresh Done !! ================================================");
/*
* Print old and new id_token, just in case.
*/
DefaultOidcUser user = (DefaultOidcUser) currentUser.getPrincipal();
log.debug("new id token is " + oidcUser.getIdToken().getTokenValue());
log.debug("old id token was " + user.getIdToken().getTokenValue());
/*
* Create new authentication(OAuth2AuthenticationToken).
*/
OAuth2AuthenticationToken updatedUser = new OAuth2AuthenticationToken(oidcUser,
oidcUser.getAuthorities(), currentUser.getAuthorizedClientRegistrationId());
/*
* Update access_token and refresh_token by saving new authorized client.
*/
OAuth2AuthorizedClient updatedAuthorizedClient = new OAuth2AuthorizedClient(clientRegistration,
currentUser.getName(), accessTokenResponse.getAccessToken(),
accessTokenResponse.getRefreshToken());
this.oAuth2AuthorizedClientService.saveAuthorizedClient(updatedAuthorizedClient, updatedUser);
/*
* Set new authentication in SecurityContextHolder.
*/
SecurityContextHolder.getContext().setAuthentication(updatedUser);
}
}
filterChain.doFilter(request, response);
}
private Boolean isExpired(OAuth2AccessToken oAuth2AccessToken) {
Instant now = this.clock.instant();
Instant expiresAt = oAuth2AccessToken.getExpiresAt();
return now.isAfter(expiresAt.minus(this.accessTokenExpiresSkew));
}
private OidcIdToken createOidcToken(ClientRegistration clientRegistration,
OAuth2AccessTokenResponse accessTokenResponse) {
JwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration);
Jwt jwt;
try {
jwt = jwtDecoder
.decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN));
} catch (JwtException ex) {
OAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, ex.getMessage(), null);
throw new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString(), ex);
}
OidcIdToken idToken = new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(),
jwt.getClaims());
return idToken;
}
}
I am open for any suggestion to improve my code. Thanks.
There are not enough details to understand your use-case fully. It would be great to understand:
Spring security is rapidly evolving around OAuth2, consider mentioning the version you are using. My answer assumes 5.2+
Are you in servlet (user logged in somehow) or non-servlet (like #Scheduled method) environment
From the limited information and my limited knowledge I have following hints:
Consider using WebClient instead of RestTemplate, this is they way to go for the future. It is reactive but don't be scared. It can be used in "blocking" environment as well, you will not use it's full potential but you can still benefit from its better support for OAuth2
WebClient itself has a ServletOAuth2AuthorizedClientExchangeFilterFunction which does pretty much what you are trying to achieve
When creating ServletOAuth2AuthorizedClientExchangeFilterFunction you pass in AuthorizedClientServiceOAuth2AuthorizedClientManager which is a strategy on how to (re)authenticate client.
Sample configuration may look as follows:
#Bean
public WebClient webClient(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientService authorizedClientService) {
AuthorizedClientServiceOAuth2AuthorizedClientManager manager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientService);
manager.setAuthorizedClientProvider(new DelegatingOAuth2AuthorizedClientProvider(
new RefreshTokenOAuth2AuthorizedClientProvider(),
new ClientCredentialsOAuth2AuthorizedClientProvider()));
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(manager);
oauth2.setDefaultClientRegistrationId("your-client-registratioin-id");
return WebClient.builder()
.filter(oauth2)
.apply(oauth2.oauth2Configuration())
.build();
}
And use it as:
#Autowire
private final WebClient webClient;
...
webClient.get()
.uri("http://localhost:8081/api/message")
.retrieve()
.bodyToMono(String.class)
.map(string -> "Retrieved using password grant: " + string)
.subscribe(log::info);
Hope this helps to move in the right direction! Have fun

Custom SAMLUserDetailsService not populating custom UserDetails

I have a Spring project and I'm converting my current authentication to use SAML2.
I have everything working as far as authentication, but I'm having difficulty in getting the SAML2 extension to insert my custom UserDetails object into the Spring Security Context authentication object.
I have a custom UserDetailsService, defined below:
public class SAMLAuthManager implements SAMLUserDetailsService {
private static final Logger logger = Logger.getLogger(JDBCAuthManager.class);
#Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
logger.info("Credential attributes: " + credential.getAttributes());
for (int x = 0; x < credential.getAttributes().size(); x++) {
Attribute attr = credential.getAttributes().get(x);
List<XMLObject> attrValues = attr.getAttributeValues();
StringBuilder strBuilder = new StringBuilder();
for (int g = 0; g < attrValues.size(); g++) {
XMLObject currObj = attrValues.get(g);
strBuilder.append(currObj.toString()).append(",");
}
strBuilder.deleteCharAt(strBuilder.length() - 1);
logger.info(attr.getFriendlyName() + ", " + strBuilder.toString());
}
String username = credential.getNameID().getValue();
userWrapper.setStaff(s);
logger.info("Returning wrapper: " + userWrapper);
return userWrapper;
} else {
return null;
}
}
}
I have also configured this userDetails in my security context config:
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
<property name="userDetails" ref="samlUserDetails" />
</bean>
However, when I inspect the SecurityContextHolder, post authentication, this line:
SecurityContextHolder.getContext().getAuthentication().getCredentials();
returns an object of type org.springframework.security.saml.SAMLCredential.
I checked to see if Spring populated the Principal with the custom object (SecurityContextHolder.getContext().getAuthentication().getPrincipal()) but it did not, that's just a String with the username populated.
Any ideas?
Thanks
The principal is by default forced to be String (in order to always permit replication of Principal which was earlier an un-serializable NameID).
This can be changed by setting forcePrincipalAsString in SAMLAuthenticationProvider to false, which will make Spring SAML include your object provided by SAMLUserDetailsService as principal in the Authentication object.
The result of call to SAMLUserDetailsService is always available under SecurityContextHolder.getContext().getAuthentication().getDetails().

Authentication of SOAP messages

I have searched for a good example and cannopt find one. I want to take the username and password from the SOAP header, and set the spring security context after I authenticate using our exisiting service methods. I have implemented the Wss4jSecurityInterceptor and it validates the header element. WHat I need to do in the callback, or some other mechanism, is create an uthetication context so I can access it later in our endpoint.
However, I dont think that the callback is the correct place to do it, as I keep getting password supplied no password errors. I am new to spring security and integration.
Config:
<bean id="SOAPSecurityInterceptor" class="com.ps.snt.ws.interceptor.SOAPSecurityInterceptor">
<property name="validationActions" value="UsernameToken"/>
<property name="validationCallbackHandler" ref="callbackHandler"/>
</bean>
<bean id="callbackHandler" class="com.ps.snt.ws.interceptor.SOAPSecurityValidationCallbackHandler">
</bean>
callback:
public class SOAPSecurityValidationCallbackHandler extends SimplePasswordValidationCallbackHandler {
#Override
protected void handleUsernameToken(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
System.out.println("In security callback " + callback.getPassword());
boolean valid = true;
String token = callback.getIdentifier();
String password = callback.getPassword();
Integer zoneID = null;
String username = null;
StringBuffer errorMessages = new StringBuffer();
if(StringUtils.isEmpty(token)) {
errorMessages.append("Username token cannot be empty");
valid = false;
} else {
Pattern pattern = Pattern.compile("^[\\w]+\\d\\d\\d\\d\\d");
Matcher matcher = pattern.matcher(token);
if(!matcher.matches()) {
valid = false;
errorMessages.append("Username token must be in the format 'user#zone'.");
}
else {
String[] parts = token.split("#");
username = parts[0];
zoneID = Integer.parseInt(parts[1]);
}
}
if(StringUtils.isEmpty(password)) {
errorMessages.append("Password cannot be empty.");
valid = false;
}
if(valid && username != null && zoneID != null) {
LoginService loginService = new LoginService();
LoginContextDO loginContextDO = loginService.getAuthenticatedLoginContext(username, password, zoneID);
AbstractAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password);
authentication.setDetails(loginContextDO);
authentication.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
System.out.println("Authetnication failed!");
}
}
}
My requirements are simple:
- validate the SOAP header (works)
- retrieve the username and password
- call our legacy service to create our login context
- set the spring security context (with logincontext as details) so I can use later in an endpoint
What mechanism can I use to validate the soap header and set a security context from that header?
SpringSecurityPasswordValidationCallbackHandler is for you. From Spring WS docs:
The SpringSecurityPasswordValidationCallbackHandler validates plain text and digest passwords using a Spring Security UserDetailService to operate. It uses this service to retrieve the (digest of ) the password of the user specified in the token. The (digest of) the password contained in this details object is then compared with the digest in the message. If they are equal, the user has successfully authenticated, and a UsernamePasswordAuthenticationToken is stored in theSecurityContextHolder. You can set the service using the userDetailsService. Additionally, you can set a userCache property, to cache loaded user details.
<beans>
<bean class="org.springframework.ws.soap.security.wss4j.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>

How to propagate spring security context to JMS?

I have a web application which sets a spring security context through a spring filter. Services are protected with spring annotations based on users roles. This works.
Asynchronous tasks are executed in JMS listeners (extend javax.jms.MessageListener). The setup of this listeners is done with Spring.
Messages are sent from the web application, at this time a user is authenticated. I need the same authentication in the JMS thread (user and roles) during message processing.
Today this is done by putting the spring authentication in the JMS ObjectMessage:
SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context.getAuthentication();
... put the auth object in jms message object
Then inside the JMS listener the authentication object is extracted and set in the context:
SecurityContext context = new SecurityContextImpl();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
This works most of the time. But when there is a delay before the processing of a message, message will never be processed. I couldn't determine yet the cause of these messages loss, but I'm not sure the way we propagate authentication is good, even if it works in custer when the message is processed in another server.
Is this the right way to propagate a spring authentication ?
Regards,
Mickaƫl
I did not find better solution, but this one works for me just fine.
By sending of JMS Message I'am storing Authentication as Header and respectively by receiving recreating Security Context. In order to store Authentication as Header you have to serialise it as Base64:
class AuthenticationSerializer {
static String serialize(Authentication authentication) {
byte[] bytes = SerializationUtils.serialize(authentication);
return DatatypeConverter.printBase64Binary(bytes);
}
static Authentication deserialize(String authentication) {
byte[] decoded = DatatypeConverter.parseBase64Binary(authentication);
Authentication auth = (Authentication) SerializationUtils.deserialize(decoded);
return auth;
}
}
By sending just set Message header - you can create Decorator for Message Template, so that it will happen automatically. In you decorator just call such method:
private void attachAuthenticationContext(Message message){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String serialized = AuthenticationSerializer.serialize(auth);
message.setStringProperty("authcontext", serialized);
}
Receiving gets more complicated, but it can be also done automatically. Instead of applying #EnableJMS use following Configuration:
#Configuration
class JmsBootstrapConfiguration {
#Bean(name = JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public JmsListenerAnnotationBeanPostProcessor jmsListenerAnnotationProcessor() {
return new JmsListenerPostProcessor();
}
#Bean(name = JmsListenerConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)
public JmsListenerEndpointRegistry defaultJmsListenerEndpointRegistry() {
return new JmsListenerEndpointRegistry();
}
}
class JmsListenerPostProcessor extends JmsListenerAnnotationBeanPostProcessor {
#Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new ListenerEndpoint();
}
}
class ListenerEndpoint extends MethodJmsListenerEndpoint {
#Override
protected MessagingMessageListenerAdapter createMessageListenerInstance() {
return new ListenerAdapter();
}
}
class ListenerAdapter extends MessagingMessageListenerAdapter {
#Override
public void onMessage(Message jmsMessage, Session session) throws JMSException {
propagateSecurityContext(jmsMessage);
super.onMessage(jmsMessage, session);
}
private void propagateSecurityContext(Message jmsMessage) throws JMSException {
String authStr = jmsMessage.getStringProperty("authcontext");
Authentication auth = AuthenticationSerializer.deserialize(authStr);
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
I have implemented for myself a different solution, which seems easier for me.
Already I have a message converter, the standard JSON Jackson message converter, which I need to configure on the JMSTemplate and the listeners.
So I created a MessageConverter implementation which wraps around another message converter, and propagates the security context via the JMS message properties.
(In my case, the propagated context is a JWT token which I can extract from the current context and apply to the security context of the listening thread).
This way the entire responsibility for propagation of security context is elegantly implemented in a single class, and requires only a little bit of configuration.
Thanks great but I am handling this in easy way . put one util file and solved .
public class AuthenticationSerializerUtil {
public static final String AUTH_CONTEXT = "authContext";
public static String serialize(Authentication authentication) {
byte[] bytes = SerializationUtils.serialize(authentication);
return DatatypeConverter.printBase64Binary(bytes);
}
public static Authentication deserialize(String authentication) {
byte[] decoded = DatatypeConverter.parseBase64Binary(authentication);
Authentication auth = (Authentication) SerializationUtils.deserialize(decoded);
return auth;
}
/**
* taking message and return string json from message & set current context
* #param message
* #return
*/
public static String jsonAndSetContext(Message message){
LongString authContext = (LongString)message.getMessageProperties().getHeaders().get(AUTH_CONTEXT);
Authentication auth = deserialize(authContext.toString());
SecurityContextHolder.getContext().setAuthentication(auth);
byte json[] = message.getBody();
return new String(json);
}
}

Resources