Spring Stomp SimpUserRegistry Anonymous Users - java

SimpUserRegistry lets you retrieve the details of all authenticated Stomp sessions, is there any such class that will let me iterate over anonymous user sessions?

Like howie described in his answer only non anonymous users will be added to the SimpUserRegistry.
But if you really want to add anonymous users also you just have to sub-class the DefaultHandshakeHandler class and override the determineUser method like stated in the Spring Doc for Version 5.0.0.M1 - Chapter 22. This should also be working for 5.1.5.Release which you are currently on when using Spring Boot 2.1.3.RELEASE:
In some cases it may be useful to assign an identity to a WebSocket session even when the user has not been formally authenticated. For example, a mobile app might assign some identity to anonymous users, perhaps based on geographical location. The do that currently, an application can sub-class DefaultHandshakeHandler and override the determineUser method. The custom handshake handler can then be plugged in (see examples in Section 22.2.4, “Deployment Considerations”).
Here is an answer (Spring websockets without principal) which shows you how you can achieve to create an AnonymousPrincipal and determine it within the custom handshake handler.
And at last you have to add your an instance of your custom handshake handler to your registered endpoint but this is depending on whether you use STOMP or not.

Following are some of the code snippets from StompSubProtocolHandler -
The handleMessageFromClient method adds the user to the stompAuthentications map and publishes a SessionConnectEvent event -
public void handleMessageFromClient(WebSocketSession session, WebSocketMessage<?> webSocketMessage, MessageChannel outputChannel) {
//...
SimpAttributesContextHolder.setAttributesFromMessage(message);
boolean sent = outputChannel.send(message);
if (sent) {
if (isConnect) {
Principal user = headerAccessor.getUser();
if (user != null && user != session.getPrincipal()) {
this.stompAuthentications.put(session.getId(), user);
}else{
//TODO try to handle here for anonymous user
}
}
if (this.eventPublisher != null) {
if (isConnect) {
publishEvent(new SessionConnectEvent(this, message, getUser(session)));
}
//...
I think you have to Check this socure code StompSubProtocolHandler, and customize it.

Related

Soteria HttpMessageContext.setRegisterSession() not working as expected?

I have implemented a custom HttpAuthenticationMechanism subclass to provide authentication using the Soteria/Java EE Security framework. I've got authentication working just fine. However, I've noticed that when I call HttpMessageContext.setRegisterSession(String, Set<String>) to create a Java EE Session, the behavior is not what I would expect. I am expecting that the authenticated identity be associated with the web Session, and my AuthenticationMechanism's validateRequest(HttpServletRequest req, HttpServletResponse res, HttpMessageContext ctx) method would not be called on subsequent requests. What I am observing, however, is that validateRequest() is called on every request, even if the user has already authenticated successfully.
I am able to get the behavior I want using the #AutoApplySession annotation on my AuthenticationMechanism class, but that is not the behavior I want. I'd like to choose whether or not to create a session based on the type of credential provided.
Is my understanding of the setRegisterSession() method incorrect? Or is this a bug within Soteria?
#AutoApplySession is the new way to do this in Soteria (JSR 375). If it does not suit your needs (as you need to either remember the authenticated identity or re-authenticate for all requests during the same HTTP session based on some other credential info), validateRequest method will still be called, regardless of whether you call the HttpMessageContext'ssetRegisterSession method or not. HttpMessageContext.setRegisterSession will make the container remember the credentials but will not reuse them automatically, you still need to make the container reuse the authentication identity by doing the same thing Soteria's AutoApplySessionInterceptor does. So in your class which implements HttpAuthenticationMechanism you should add the following code before your actual authentication logic is performed in the validateRequest method:
Principal userPrincipal = request.getUserPrincipal();
if (userPrincipal != null) {
httpMessageContext.getHandler().handle(new Callback[] {
new CallerPrincipalCallback(httpMessageContext.getClientSubject(), userPrincipal) }
);
return AuthenticationStatus.SUCCESS;
}
Also, see this answer by Arjan Tijms. Although it's about JASPIC not Soteria, but in this case I think it's relevant.
Hope this helps.
I thing you're following the incorrect source you've look at this IMPLEMENTATION.
/* (non-Javadoc)
* #see javax.security.authenticationmechanism.http.HttpMessageContext#setRegisterSession(java.lang.String, java.util.Set)
*/
#Override
public void setRegisterSession(String username, Set<String> groups) {
Jaspic.setRegisterSession(messageInfo, username, groups);
}
Under the library location:
import org.glassfish.soteria.mechanisms.jaspic.Jaspic;
From the mechanisms.

How to get current user authentication inside UserDetailsService

In my application I am trying to unite ActiveDirectory authentication with OAuth2 refresh tokens.
I was able to successfully authenticate via ActiveDirectoryLdapAuthenticationProvider. I have also provided my custom implementation of LdapUserDetailsMapper that populates the UserDetails with some custom attributes taken from ActiveDirectory. Key thing here is that these attributes have a confidentialty flag set on them and are only available to the user itself (i.e. authenticated user could read the values of these attributes for himself but not for the others). These attributes are stored in Authentication object and are used by an application in a context of an authenticated user.
Things get tricky when I try to add refresh tokens to the picture. Refresh tokens require me to implement a UserDetailsService where I have to provide new UserDetails having just a user name. This is not feasible due to confidentialty flag. Even if I have some master account in my application with the ability to browse ActiveDirectory I will not be able to retrieve the confidential attributes.
So I would rather prefer to provide more atomic implementations like the function that checks if the user is still active or the function that provides a renewed set of user authorities. Unfortunately I did not find this level of atomicity in Spring Security. So it looks like for refresh tokens I have to provide an implementation of UserDetailsService.
If I have to provide new user details I would like to have an access to previous user Authentication object. In this case I will check the user and if it is still active I will copy all the confidential information from previous Authentication. The problem is that it does not seem to be available. At the moment when UserDetailsService::loadUserByUsername() is called SecurityContextHolder.getContext() does not contain the user authentication. Authentication is also not available from UserDetailsService API - I only get the user name. At the same time user's Authentication object is present just one stack frame up in UserDetailsByNameServiceWrapper class:
public UserDetails loadUserDetails(T authentication) throws UsernameNotFoundException {
return this.userDetailsService.loadUserByUsername(authentication.getName());
}
The least thing I want to do here is to implement some in-memory storage for all user confidential information to be used whenever I need to provide new UserDetails. I already have all the required information in user authentication managed by Spring and doing this on my end seems to be just surplus.
And here comes question list:
If you feel that I am doing something terribly wrong from the perspective of application security architecture, please tell me
Is there a way to tell Spring during refresh token procedure to use previous UserDetails object so that application could just answer the question if the user is still active and should be issued a new access token (and not provide the UserDetailsService at all)?
Is there a way to get previous user Authentication object during the call to UserDetailsService::loadUserByUsername() so that I could use it as a source of confidential info?
Is there some other approach that I do not see at the moment to add refresh tokens to my application?
Update:
Here I saw a comment that you could implement your own AuthenticationUserDetailsService to work around the problem. This I do not see how to do. It is hardcoded in AuthorizationServerEndpointsConfigurer that it always creates an instance of UserDetailsByNameServiceWrapper so to provide your own implementation you would have to interfere into AuthorizationServerEndpointsConfigurer initialization process.
OK, looks like the answer with Spring Security 4.0 is you can't.
So I had to apply the following hack which works, but I do not like it very much. Since it works I am posting it here. Since it does not solve the original problem, but works around it I will not mark it as accepted by the author.
Switch to JWT tokens
Use custom TokenEnhancer to inject all information that is required to recreate the user (user secret in my case) to the token directly. Of course, the value must be encrypted by the server with symmetrical crypto algorithm before adding it to the token.
Instruct authorization server to use custom AccessTokenConverter. This implementation of AccessTokenConverter would extract the secret value from the token, decrypt it and put it to ThreadLocal field.
Instruct custom UserDetailsService to retrieve the user secret from the ThreadLocal field set in step 3. This is the best way I found so far to deliver the current authorization context to UserDetailsService. And this is the part that I do not like most in my solution.
Use custom security filter to erase the value set in step 3 from ThreadLocal field.
P.S. I still do not see the possibility to implement custom AuthenticationUserDetailsService that was mentioned earlier. If such possibility exists it could have been another way to solve the problem.
Some useful links:
Extending Spring Security OAuth for Multi-Tenant
Detailed explanation of the problem in spring-security-oauth GitHub
I've got the response from Joe Grandja on spring-security-oauth github page.
Posting it here since it actually provides an answer to the original question.
Hi #masm22. To help with question 1 and 2, below is a custom configuration that will allow you to hook into the refresh_token grant and provide your own behaviour or delegate to super to proceed with current behaviour. It will also allow you to access the user Authentication so you can read your custom (confidential) attributes.
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
..... // other config
#Autowired
private ClientDetailsService clientDetailsService;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenServices(this.customTokenServices());
}
private DefaultTokenServices customTokenServices() {
DefaultTokenServices tokenServices = new CustomTokenServices();
tokenServices.setTokenStore(new InMemoryTokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(true);
tokenServices.setClientDetailsService(this.clientDetailsService);
return tokenServices;
}
private static class CustomTokenServices extends DefaultTokenServices {
private TokenStore tokenStore;
#Override
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) throws AuthenticationException {
OAuth2RefreshToken refreshToken = this.tokenStore.readRefreshToken(refreshTokenValue);
OAuth2Authentication authentication = this.tokenStore.readAuthenticationForRefreshToken(refreshToken);
// Check attributes in the authentication and
// decide whether to grant the refresh token
boolean allowRefresh = true;
if (!allowRefresh) {
// throw UnauthorizedClientException or something similar
}
return super.refreshAccessToken(refreshTokenValue, tokenRequest);
}
#Override
public void setTokenStore(TokenStore tokenStore) {
super.setTokenStore(tokenStore);
this.tokenStore = tokenStore;
}
}
}
The other thing I want to point out for your information is in DefaultTokenServices.refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
has the following code:
OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
if (this.authenticationManager != null && !authentication.isClientOnly()) {
// The client has already been authenticated, but the user authentication might be old now, so give it a
// chance to re-authenticate.
Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());
user = authenticationManager.authenticate(user);
Object details = authentication.getDetails();
authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
authentication.setDetails(details);
}
The user is being re-authenticated. Possibly something you may want to do in your custom implementation if need be.

Spring REST Api -- access User details in repository

The setup of the RESPApi project is:
SpringBoot
Spring's OAuth2
In the project we have many clients, so SQL queries almost always have "... and clientId = ?" in the where clause.
We store clientId in the SecurityContext with other user details (we extend Spring's User class).
The question is: how to get the User object in the #Repository?
Possible solutions we can think of:
In every repository implementation add
SecurityContextHolder.getContext().getAuthentication()
cast the result to our custom UserDetails implementation and use it.
Cons: somehow I feel there's a better solution.
Add #AuthenticationPrincipal annotated parameters to the controllers and then pass the parameters to the service layer and then to the repository layer.
Cons: passing the paremeter though 2 layers only to obtain clientId doesn't seem reasonable.
I thought about #Autowired paramter MyUser user in the #Repository class. The first try was to create #Configuration annotated class in which there will be a method
#Bean
public MyUser getUser() {
SecurityContext context = SecurityContextHolder.getContext();
if (context != null) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
return (MyUser) authentication.getPrincipal();
}
}
return null;
}
But the bean is null and I cannot use it.
For now we've ended up with solution nr 1 but I feel there must be a better way.
Any ideas how to solve this problem?
If you're using Spring Data (or have the time to switch to using it), you can use the SecurityEvaluationContextExtension and use principal directly in your queries:
https://stackoverflow.com/a/29692158/1777072
If not, you could hide the static access if it offends (or if you want more control over it changing in future):
#Component
public class AuthenticationHelper {
public Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
Then inject that class into your Repository.
Or your Service. That's probably a better fit than the Repository.
I like to keep Repositories dumb (ultimately using Spring Data to avoid writing them entirely).
And I like to think of Services as being separated out of the web layer, running on separate boxes (even if they aren't). In that situation, you would never pass the Authentication details over HTTP from Controller to Service. The service would obtain authentication details for itself, rather than just trusting what the web layer sent it.
So I think the Service should get the details itself, rather than the Controller passing them through.
Your bean is null because by default beans are singleton and they are created when the application starts, and as you can imagine, you are not going to have a SecurityContext at that point.
Try declaring your bean with request scope, in this way:
#Bean
#Scope(value=WebApplicationContext.SCOPE_REQUEST, proxyMode=ScopedProxyMode.TARGET_CLASS)
public MyUser getUser() {
.....
}

Pre-authenticated user in Java EE/JBoss AS 6

I am migrating some Java EE modules from Spring to EJB and are now facing the problem that I need some sort of pre-authentication prior to calling a service method.
The problem is actually quite easy. A call comes in from an internal protocol handler (some thread started the proprietary protocol handler and received requests using a custom TCP protocol). Not this connection already authenticated the user and wants to call a service method next. This service method requires a principal information (user name) for processing.
So in Spring we simply pushed the SecurityContext to the local thread and removed it when the call was done.
Protocol Handler -> Set SecContext -> Call -> Remove SexContext -> End
Is there anything similar to that in Java EE/JBoss? I know there are "#RunAs" constructs but I don't know if they can be used programmatically. Or is there a way to "log in" using the JAAS LoginContext class? But how do I configure JAAS then?
If this is purely a matter of getting an identity into the JAAS context, you should be able to do something like this:
final String username; // get this from somewhere
Princpal principal = new Principal() {
public String getName() {
return username;
}
};
Subject subject = new Subject(true, Collections.singleton(principal), Collections.emptySet(), Collections.emptySet());
Subject.doAs(subject, new PrivilegedAction<Void>() {
public Void run() {
// do your method call here
}
});
Note that you can return a value from the PrivilegedAction by binding it to a type other than Void, and throw an exception by implementing PrivilegedExceptionAction instead.
Obviously if you have a more sophisticated idea of what a principal is, you could use that (implementing toString, hashCode, and equals would be a good idea).

Multiple java servlet authentication methods

Is it possible to have multiple authentication methods for a java servlet? For example, have form based authentication in addition to open id based authentication so users can choose how they log in.
Yes.
However, I would suggest doing this using servlet filters instead of on the servlet itself.
http://brendangraetz.wordpress.com/2010/06/17/use-servlet-filters-for-user-authentication/
Follow the steps in that post, and override the isAuth() method such that it performs the authentication in however many modes you wish. In (very rough, untested) code:
#Override protected boolean isAuth()
{
String authMode = (String)(getSession(true).getAttribute("authMode"));
if (authMode == null) { return false; }
if (authMode.equals("open id") {
//do open id authentication steps here
//return true if authentication passes
}
else if (authMode.equals("some other authentication") {
//do some other authentication steps here
//return true if authentication passes
}
...
return false;
}
I am assuming of course that you already know how to implement the authentication steps in each mode individually.
The "trick" is to store a value in the HTTP session, immediately after the user performs the log in authentication, in the HTTP session. Based on this value, the filter will know what it should check or query whatever you specify before loading the servlet.
Another way of performing multiple authentication is with JAAS, the Java Authentication and Authorization service. Using JAAS, you can stack various authentication modules on top of each other, and you can configure which authentication module to run and which not to. This is called PAM (pluggable authentication module). Search for "J2SE JAAS" on Google or take a look at http://server.pramati.com/docstore/1270002/index.htm. These should help you to get started if you decide to go this route.
Yes it is possible, but it tends to be a bit tricky to implement.
For example, out-of-the-box SpringSecurity has support for local authentication, OpenId, X509 and other schemes, but combining them so that the user has alternative ways of logging in requires custom classes and custom wiring.

Categories