How to have Spring Security enabled for an application using third party login? - spring

I have a Spring Boot enabled application whose login is controlled by third party Siteminder application. After successful authentication, Sitemeinder redirects to our application url. We fetch the HttpRequest from Siteminder and process the requests.
Now, how can Spring security be enabled in this case for authorizing users based on roles.
#Controller
public class LoginController
#RequestMapping( value= "/")
public void requestProcessor(HttpServletRequest request)
{
.
.
.}
The above controller's request mapper reads the request coming from SiteMinder and processes the request which has the Role of the user logged in. Where can we have Spring Security enabled to authorize pages and service methods to the user.

This is an scenario for the PreAuthenticated security classes:
Take a look here:
http://docs.spring.io/spring-security/site/docs/current/reference/html/preauth.html

Spring Security processes request before it gets to your controller in a filter configured in spring security configuration.
There is a documentation on how to configure spring security with SiteMinder.
The rules in your configuration will define the access to resources

Depends what you get in session. If somehow u can to take user and password from session you can authenticate user directly from code as :
#Autowired
AuthenticationManager authenticationManager;
...
public boolean autoLogin(String username, String password) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
Authentication auth = authenticationManager.authenticate(token);
if (auth.isAuthenticated()) {
logger.debug("Succeed to auth user: " + username);
SecurityContextHolder.getContext().setAuthentication(auth);
return true;
}
return false;
}

Related

How to get current logged in user in Spring Cloud microservices?

I have a Spring Cloud application with Gateway microservice powered with Zuul. Gateway main responsibility is to authenticate and authorize users with Spring Security and JWT.
Now in other microservices that are behind the Gateway, I want to get current logged in user. Any idea?
I implemented a similar architecture. I achieved this in below way
Step 1 - Configure Zuul to pass headers to downstream systems by configuring sensitive headers in application properties.
zuul.sensitiveHeaders=Cookie,Set-Cookie
Step 2 - Expose an endpoint in gateway that returns currently logged in user details
public UserReturnData fetchUserDetails()
{
UserReturnData userReturnData = new UserReturnData();
List<String> roles = new ArrayList<String>();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
userReturnData.setUsername(auth.getName());
Long id=uRepo.findId(auth.getName());
userReturnData.setId(id);
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder
.getContext().getAuthentication().getAuthorities();
for (SimpleGrantedAuthority authority : authorities) {
roles.add(authority.getAuthority());
}
userReturnData.setRoles(roles);
return userReturnData;
}
Step 3 - From your micro-service, make API call (using resttemplate or web-client) to this exposed endpoint with bearer string in header.
#Service
public class UserDetailsService
{
#Autowired
WebClient.Builder webClientBuilder;
#Autowired
HttpServletRequest request;
#Value("${common.serverurl}")
private String reqUrl;
public UserReturnData getCurrentUser()
{
UserReturnData userDetails = webClientBuilder.build()
.get()
.uri(reqUrl+"user/me")
.header("Authorization", request.getHeader("Authorization"))
.retrieve()
.bodyToMono(UserReturnData.class)
.block();
return userDetails;
}
}
This is if you want to keep it simple. If you have more complex requirements, you can use zuul filters.

Spring Cloud Gateway Oauth2Login Return JWT Token Instead of SESSION Cookie Upon Successful Login

sorry in advance if the question is previously asked, but I have not been able to find an answer.
I am trying to setup Spring Cloud Gateway to act as a OAuth2 client to authenticate/login users via a Keycloak Authentication server. I have been able to achieve this using the following code snipet:
Security Config:
#Configuration
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class SecurityConfig {
private final GatewayAuthenticationSuccessHandler gatewayAuthenticationSuccessHandler;
public SecurityConfig(GatewayAuthenticationSuccessHandler gatewayAuthenticationSuccessHandler) {
this.gatewayAuthenticationSuccessHandler = gatewayAuthenticationSuccessHandler;
}
#Bean
public SecurityWebFilterChain securityWebFilterChain(
ServerHttpSecurity http,
ReactiveClientRegistrationRepository clientRegistrationRepository) {
http
.authorizeExchange()
.pathMatchers("/ui/**").permitAll()
.anyExchange().authenticated()
.and()
.oauth2Login().authenticationSuccessHandler(gatewayAuthenticationSuccessHandler)
.and()
.oauth2ResourceServer().jwt();
http.logout(
logout ->
logout.logoutSuccessHandler(
new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository)));
http.logout().logoutUrl("/logout");
http.csrf().disable();
http.httpBasic().disable();
http.formLogin().disable();
return http.build();
}
}
Auth Success Handler:
#Component
public class GatewayAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {
private ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();
#Value("${my.frontend_url}")
private String DEFAULT_LOGIN_SUCCESS_URL;
#Override
public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
URI url = URI.create(DEFAULT_LOGIN_SUCCESS_URL);
return this.redirectStrategy.sendRedirect(webFilterExchange.getExchange(), url);
}
}
With this setup, the gateway app can authenticate the users and obtain a JWT token from the authentication server on behalf of the caller (UI app). Based on my understanding, Spring security then uses spring session to create and feed back a SESSION cookie to the caller. This session cookie can be used for subsequent calls to authenticate the user. The gateway would use the SESSION cookie value to retrieve the associated JWT token from the cache and relays it to the downstream resource servers when proxying requests. I have also setup a token refresh filter to refresh the JWT token on the caller's behalf and a Redis ache to share this session cookie between multiple instances of the gateway.
What I would like to do now is to return the actual JWT token that was retrieved by the gateway back to the caller (instead of a SESSION cookie). In other words I am hoping to make my gateway a little more stateless by using JWT end-to-end (instead of using SESSION cookie for caller --> gateway and then JWT for gateway --> resource servers). Is this even possible with the current state of spring cloud gateway?
PS. I am using spring boot version 2.2.8 and spring cloud version HOXTON.SR6
Not sure this can help , but try to add a SessionPolicy as STATELESS to your webfilter chain as shown below , and it should work.
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
Also you could try to override the sessionAuthenticationStrategy with a NullAuthenticatedSessionStrategy if you are extending your config class to WebSecurityConfigurerAdapter.
override fun sessionAuthenticationStrategy(): SessionAuthenticationStrategy {
return NullAuthenticatedSessionStrategy()
}

Is it good idea to take user id from SecurityContextholder in spring boot RestController..?

I am developing spring boot rest application for an ecommerce app, suppose i have endpoint /shipping-address which will fetch all the saved addresses for the user, is it good idea to take user id from SecurityContextHolder like
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Long userId;
if (principal instanceof UserPrincipal) {
userId = ((UserPrincipal) principal).getId();
}
or should i pass from the client side in the request body..? which is correct..? If i take from SecurityContextHolder is it problem when it comes to Horizontal scaling..??
Please help, i am new to backend development. Thanks.
Taking the userId from the SecurityContext is a good idea because it will prevent from hacking your application.
If you pass the userId from the client somebody could intercept the request and change the userId.
In regards to scaling it depends how you authenticate the user. If it's basic or token based and does not rely on session information. Everything will be fine.
SecurityContext
There is no problem in using a SecurityContext with Spring MVC.
You could use something like :
#RestController
#RequestMapping(path = "/auth")
#Slf4j
public class AuthResource {
#GetMapping(path = "whoami", produces = MediaType.APPLICATION_JSON_VALUE)
#PreAuthorize("isAuthenticated()")
public ResponseEntity<String> whoami(#AuthenticationPrincipal() UserDetails userDetails) {
String login = userDetails.getUsername();
return ResponseEntity.ok(login);
}
}
The annotation #AuthenticationPrincipal from Spring Security will simplify your code.
Session storage
By default, the session will be backed by the HttpSession (cookie JSESSIONID).
So you'll need something like sticky sessions if using a load balancer.
However, sessions can be stored elsewhere, like in relational databases (JDBC) or in Redis : this is what Spring Session can do.
See also Control the Session with Spring Security.
You can also choose to not use sessions with Spring Security :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

How to do LDAP authentication on Post Request

For my application, I use Spring boot and LDAP to authentication user. And I have Spring Security to control the API.
Basically what I do is to attach the Authentication: basic on the header and call the get user api(/user, get mapping) to get the user details. From there I return the pricinpal and use the user session to access the application.
#GetMapping("/user")
public Principal user(Principal user) {
return user;
}
My question is how to do authencation on post? Same header with authentication, but the only difference is /user endpoint will be post mapping. From postman I got 401 error. Somehow LDAP isn't picking up the authentication header to do authentication.
#PostMapping("/user")
public Principal user(Principal user) {
return user;
}
Please Help. And Thanks in advance.

Spring OAuth - Reload resourceIds and authorities of authentication

I just apply Spring Boot and Spring Cloud to build a microservice system. And I also apply Spring Oauth to it. Honestly, everything is perfect. Spring does a great job in it.
In this system, I have a microservice project does the job of an OAuth server, using JDBC datasource, and I using Permission based for UserDetails authorities (1 User has several Permissions). There are several microservice project does the jobs of Resource server (expose Rest api using Jersey), access security is based on Permissions of Authentication of OAuth bearer token.
Resource Server OAuth config class is something like this
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/restservice/object/list")
.hasAuthority("PERMISSION_VIEW_OBJECT_LIST");
// ...
}
#Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.resourceId("abc-resource-id")
.tokenStore(new JdbcTokenStore(dataSource()));
}
#Bean
#ConfigurationProperties(prefix = "oauth2.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
Everything is great! But I encounter 2 problems:
If I add a new microservice project as a new resourceId, and I append resourceId value to RESOURCE_IDS in table OAUTH_CLIENT_DETAILS of the OAuth client, all requests to Rest API of new resource service return error something like this
{"error":"access_denied","error_description":"Invalid token does not contain resource id (xyz-resource-id)"}
This happens even when user logout and re-login to obtain new access token. It only works if I go to delete records of the Access token and Refresh token int table OAUTH_ACCESS_TOKEN and OAUTH_REFRESH_TOKEN in database.
If at runtime, Permission of a User is changed, the authorities of authentication is not reloaded, I see that AUTHENTICATION value of the Access Token in table OAUTH_ACCESS_TOKEN still contains old Authorities before Permission is changed. In this case, User must logout and re-login to obtain new Access Token with changed authorities.
So, are there any ways to fix these 2 problems.
I'm using Spring Cloud Brixton.SR4 and Spring Boot 1.3.5.RELEASE.
If you are using the default Spring JdbcTokenStore, then the users authentication is serialised and stored with the access/refresh token when the user authenticates and retrieves their token for the first time.
Each time the token is used to authenticate, it is this stored authentication that is loaded which is why changes to the user permissions or the addition of extra resources is not reflected in the users permissions.
In order to add in some checking on this, you can extend DefaultTokenServices and override the loadAuthentication(String accessTokenValue) method to perform your own checks once the users authentication is loaded from the token store.
This may not be the ideal way of doing this, but it is the only way we've found of doing it so far.
To override DefaultTokenServices, add the follwoing bean method to you AuthorizationServerConfigurerAdapter config class:
class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Bean
public AuthorizationServerTokenServices authorizationServerTokenServices() throws Exception {
// Where YourTokenServices extends DefaultTokenServices
YourTokenServices tokenServices = new YourTokenServices();
tokenServices.setTokenStore(tokenStore);
tokenServices.setClientDetailsService(clientDetailsService);
return tokenServices;
}
}
I resolved reload problem this way.
#Bean
public ClientDetailsService jdbcClientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}

Resources