Sending message to specific user using spring - spring

My Goal - To send message to single user if possible without using spring security
I want to input a username from user and set it as username in spring security so that I can use method convertAndSendToUser. I searched on the net and found two approaches
Using DefaultHandshakeHandler to set username but this way I am unable to retrieve user input from the page and use it in determineUser method
I have tried using following piece of code
Authentication request = new UsernamePasswordAuthenticationToken("xyz", null);
SecurityContextHolder.getContext().setAuthentication(request);
But it is not working as it is just changing the username for that method and then it resets the username.
If possible is there any approach with which I can send message to single user without using spring security. Thanks in advance
P.S. I am a newbee.

You can use your first approach to set the Username. First you need add the interceptor to your StompEndpointRegistry class and after that you can determine User from the attributes Map and return the Principal.
Below is the Code:
HttpSessionHandshakeInterceptor is Used for Intercepting the Http attributes and provide them in the DefaultHandshakeHandler class
#Configuration
#EnableWebSocketMessageBroker
#EnableWebMvc
#Controller
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app","/user");
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat")
//Method .addInterceptors for enabling interceptor
.addInterceptors(new HttpSessionHandshakeInterceptor())
.setHandshakeHandler(new MyHandler())
.withSockJS();
}
class MyHandler extends DefaultHandshakeHandler{
#Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler,
Map<String, Object> attributes) {
//Get the Username object which you have saved as session objects
String name = (String)attributes.get("name");
//Return the User
return new UsernamePasswordAuthenticationToken(name, null);
}
}
}

Related

Spring Boot not blocking request to the endpoint

Im having an issue with spring boot. I am trying to block one specific endpoint called /users/name/ but when i configure it on httpSecurity I can still call the endpoint. I need to block this specific endpoint below is my code.
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthConfigClass extends
WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests().antMatchers("/users/name/").permitAll()
.anyRequest().authenticated().and().httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
{
auth.inMemoryAuthentication().withUser("admin")
.password("{noop}password").roles("USER");
}
}
And this is the RestController. Note please that the intention of this app is to make it vulnerable to attacks like the OWASP TOP API so no worries about security issues please tough I accept suggestions.
#Api(value="Users Endpoint and maintenance only for prvileged users")
#RequestMapping("/users")
public class RestControllerMain {
private final UserRespository userRespository;
#Autowired
public RestControllerMain(UserRespository userRespository) {
this.userRespository = userRespository;
}
//Excessive Data Exposure OWASP TOP 10
#RequestMapping(value="/", method=RequestMethod.GET)
public Iterable<User> getAllUsers() {
return userRespository.findAll();
}
#RequestMapping(value="/", method=RequestMethod.POST)
public void UserInsert(#RequestBody User user) {
userRespository.save(user);
}
//null pointer exception and SQL injection OWASP TOP 10 API.
#RequestMapping(value="/name/{user}", method=RequestMethod.GET)
public String mainUser(#PathVariable ("user")String username) {
if(!username.matches("/[\\t\\r\\n]|(--[^\\r\\n]*)|(\\/\\*[\\w\\W]*?(?=\\*)\\*\\/)/gi\n" )) {
return "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘‘VALUE’’.";
}
return "SQL Injection not found";
}
//XSS Also in the OWASP TOP API.
#RequestMapping(value="/search", method=RequestMethod.GET)
public String getMeUSer(#RequestBody User user) {
return "Nice to meet you" + user.getName();
}
// OWASP TOP 10 API. Broken Object Level
#RequestMapping(value="/{id}")
public Optional<User> getUserById(#PathVariable Long id) {
return userRespository.findById(id);
}
}
Please help me figure this I ended up following a tutorial.
As I understood, you want to require authentication for "users/name/{user}" endpoint, but your configuration states
.antMatchers("/users/name/")
whereas it should be
.antMatchers("/users/name/**")
where "**" means any matching pattern. But you if want to grant access after checking the privileges, as you stated in the description of controller, you should configure Spring's authorization and add
#Secured("ROLE_VIEWER, ROLE_ADMIN")
before service or controller methods, which will block any user who doesn't have those roles.

Idiomatic way to register custom Authentication in Spring Security

My use case is that I'm using Spring Security 5.2's Oauth2 login, but would like my database user class to be available alongside the Oauth2AuthenticationToken within the Authentication. This is so that I have my database user class cached by the SecurityContextHolder.
In Pseudocode:
A user logs in using Google or Github Oauth2
My app finds (or creates) the database user with the information returned
My app saves to the SecurityContextHolder a custom Authentication wrapper that wraps both the Oauth2AuthenticationToken and the database User class
On subsequent requests, the custom Authentication wrapper is available to controller methods
Here are my attempts at a wrapper:
class MyAuthenticationWrapper implements Authentication {
public MyAuthenticationWrapper(User user, Authentication underlyingAuth1) {
this.user = user;
this.underlyingAuth = underlyingAuth1;
}
private final User user;
private final Authentication underlyingAuth;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return underlyingAuth.getAuthorities();
}
#Override
public void setAuthenticated(boolean isAuthenticated) {
underlyingAuth.setAuthenticated(isAuthenticated);
}
#Override
public String getName() {
return underlyingAuth.getName();
}
#Override
public Object getCredentials() {
return underlyingAuth.getCredentials();
}
#Override
public Object getPrincipal() {
return underlyingAuth.getPrincipal();
}
#Override
public boolean isAuthenticated() {
return underlyingAuth.isAuthenticated();
}
#Override
public Object getDetails() {
return underlyingAuth.getDetails();
}
public User getUser() {
return user;
}
}
And a custom Oauth2AuthenticationFilter:
#Component
class CustomLoginAuthenticationFilter extends OAuth2LoginAuthenticationFilter {
#Autowired
private UserDAO userDAO;
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
Authentication auth = super.attemptAuthentication(request, response);
if (auth instanceof OAuth2AuthenticationToken) {
switch (((OAuth2AuthenticationToken) auth).authorizedClientRegistrationId) {
case "google":
Optional<User> user = userDAO.findByEmail(username);
if (!user.isPresent()) {
throw new NotFoundException("!");
}
return MyAuthenticationWrapper(auth, user.get());
}
}
return auth;
}
}
I haven't had success getting this approach to work, and I'm left wondering if this is the right approach at all.
Is there another, more idiomatic approach to combining database user data with Oauth2 user data in Spring security?
Perhaps looking into OAuth2UserService might help. It gets invoked after successfully obtaining the OAuth token. This is how it would work:
A user logs in using Google or Github Oauth2
No need to add anything. Let the default filters take care of that.
My app finds (or creates) the database user with the information returned
Create your own OAuth2UserService as a bean (it'll get picked up automatically) that takes care of dealing with the database:
#Component
public class CustomService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
#Override
public OAuth2User loadUser(OAuth2UserRequest userRequest)
throws OAuth2AuthenticationException {
// ... DB logic goes here
}
}
In loadUser(...), the OAuth2UserRequest gives you access to the corresponding ClientRegistration and the OAuth2AccessToken, which you can then use to query or update the database.
My app saves to the SecurityContextHolder a custom Authentication wrapper that wraps both the Oauth2AuthenticationToken and the database User class
No need to deal with a wrapper! The custom OAuth2User you construct from information from the database will be the Principal in the OAuth2LoginAuthenticationToken, which ends up being the Authentication, so it'll be available to your application. Since you're not dealing with the Authentication yourself, you wouldn't have to worry about saving it in the SecurityContextHolder.
On subsequent requests, the custom Authentication wrapper is available to controller methods
Your Authentication will be of type OAuth2LoginAuthenticationToken. You can get your custom OAuth2User like this:
OAuth2LoginAuthenticationToken auth = //...
OAuth2User user = auth.getPrincipal();
For more info on the core classes you're dealing with, these might be helpful:
OAuth2LoginAuthenticationFilter
OAuth2LoginAuthenticationProvider
OAuth2UserRequest
OAuth2LoginAuthenticationToken
For out-of-the-box implementations of OAuth2UserService, check out:
DefaultOAuth2UserService
CustomUserTypesOAuth2UserService

How to pass attribute to Spring Controller in a stateless application?

I am currently implementing a SAML SSO solution in my application where in my SAMLUserDetailsService, I am loading my user
#Service
public class SAMLUserDetailsServiceImpl implements SAMLUserDetailsService {
#Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
return new User(credential.getNameID().getValue());
}
}
I am then using a SavedRequestAwareAuthenticationSuccessHandler to redirect user to a landing controller upon successful authentication.
#Bean
public AuthenticationSuccessHandler successRedirectHandler() {
SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler =
new SavedRequestAwareAuthenticationSuccessHandler();
successRedirectHandler.setDefaultTargetUrl("/landing");
return successRedirectHandler;
}
Controller:
#RequestMapping("/landing")
public ResponseEntity landing(User user) {
return ResponseEntity.ok(user.getLoginName());
}
Is there a way to pass the User object to my controller. I noticed that this is usually done using a HandlerMethodArgumentResolver but since my application is stateless and does not use sessions, is there a way to achieve this using another way please?
You don't need injection for this. Use following instead:
SecurityContextHolder.getContext().getAuthentication().getName()
or
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
In the latter case check the type of what getPrincipal() returned. It can be String, it can be UserDetails. If latter, cast it to UserDetails and call getUsername().

Cache user specific application data with Spring Security

In our microservice each authenticated Spring Security user has an associated application-specific data structure.
when thinking on how can we easily cache this data together with the user, we thought it would be good if it could have been done similar to this:
add the cached data to the in-memory-authentication when creating the users:
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.inMemoryAuthentication().withUser("user").password("123").roles("ROLE");
auth.inMemoryAuthentication().withUser("user").cache(appDate);
...
}
pull the data in #RestController methods:
#RequestMapping("/foo")
public void foo() {
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Object details = SecurityContextHolder.getContext().getAuthentication().getDetails();
Object cachedAppDate= SecurityContextHolder.getContext().getAuthentication().getCachedData();
}
Obviously the method in bold are a wish-list and do not exist.
Any advice on how to do this easily with the existing Spring Security framework?
thanks!
If you can get your cached data using only user name (no password required) you can use implement org.springframework.security.core.userdetails.UserDetailsService, it has only one method - UserDetails loadUserByUsername(String username), and return required data as custom UserDetail object. After implement interface, just pass it as UserDetailService for AuthenticationManagerBuilder.
If you need password to get that cached data, things got a bit complicated.
You should create your own AuthenticationProvider and put cached data in Principal or UserDetails. For example code for set additional data in Principal:
public class MyProvider implements org.springframework.security.authentication.AuthenticationProvider {
#Override
public boolean supports(Class<? extends Object> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
... // check login/password
Object cachedAppDate = "i'm cached data!";
MyUser user = new MyUser(token, cachedData);
UsernamePasswordAuthenticationToken output = new UsernamePasswordAuthenticationToken(user, authentication.getCredentials(), user.getAuthorities());
output.setDetails(authentication.getDetails());
return output;
}
}
public static class MyUser extends org.springframework.security.core.userdetails.User {
private final Object cachedData;
public User(UsernamePasswordAuthenticationToken token, Object cachedData) {
super(token.getName(), "", token.getAuthorities());
this.cachedData = cachedData;
}
public Object getCachedData() {
return this.cachedData;
}
}
and access cached data as ((MyUser)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getCachedData();

Spring - Call a Service method in JSTL

I'm using Spring Security to handle user authentication for my Spring MVC web app. I'm able to get the username from the Authentication object, but my username is the email address, and I want to be able to show the user's actual name in my header.
So I have my custom User class:
class Users{
String name;
String email;
String password;
// getters and setters
}
I thought about using an aop scoped proxy to set the User in the session, as explained in this blog: http://richardchesterwood.blogspot.co.uk/2011/03/using-sessions-in-spring-mvc-including.html . The problem I faced using this approach is that the AuthenticationSuccessHandler is actually a Service and should be stateless. So Spring doesn't autowire a Users object for me in the Service.
So I created a Service method that would get the username (or email) from the Authentication object and return my Users object. This I can use in my Controllers.
#Service
#Transactional
public class UserServiceImpl implements UserService {
#Override
public Users getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User userD = (User)auth.getPrincipal();
Users currentUser = getUserByEmail(userD.getUsername());
return currentUser;
}
}
So is there a way that I can call this Service method from JSTL to get the user's full name, which I can display in my header?
Am also open to suggestions for a better way to implement this.
EDIT:
In my earlier approach using the AuthenticationSuccessHandler, my code goes like this:
#Service("userDetailsService")
#Transactional
public class UserAuthenticationServiceImpl implements AuthenticationSuccessHandler {
#Autowired
Users currentUser;
#Override
public void onAuthenticationSuccess(HttpServletRequest hsr, HttpServletResponse hsr1, Authentication a) throws IOException, ServletException {
User user = (User) a.getPrincipal();
Users user1 = userDao.getUserByEmail(user.getUsername());
currentUser.setName(user1.getName());
currentUser.setUserRoles(user1.getUserRoles());
//currentUser = user1;
}
}
And in my spring-servlet.xml file, I have this:
<bean id="currentUser" class="com.foo.bean.Users" scope="session">
<!-- this next element effects the proxying of the surrounding bean -->
<aop:scoped-proxy/>
</bean>
The problem I'm facing here is that Spring isn't autowiring my currentUser object because the Service isn't in the session scope.
If the only thing you need is the full name just use an AuthenticationSuccessHandler to retrieve the user and add the name to the session (or the full user if you need more then that).
#Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication auth) throws IOException, ServletException {
User user = (User) auth.getPrincipal();
Users user1 = userDao.getUserByEmail(user.getUsername());
WebUtils.setSessionAttribute(req, "currentUser" user1);
}
Then in your JSP the only thing you need is ${currentUser.username}.
Although I wouldn't suggest stuffing the full user in the session I would suggest just adding the information needed.
WebUtils.setSessionAttribute(req, "currentUsername" user1.getUsername());
Then in your JSP ${currentUsername} saves you a lot of serialization overhead of the session.

Resources