How does spring security pass credentials? / Postman - spring

I just created a basic layer to secure my Spring API.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
#Override
public UserDetailsService userDetailsService() {
List<UserDetails> users = new ArrayList<>();
users.add(User.withDefaultPasswordEncoder().username("Admin").password("xxx!!").roles("ADMIN").build());
users.add(User.withDefaultPasswordEncoder().username("Pharmacist").password("xxx!!").roles("PHARMACIST").build());
users.add(User.withDefaultPasswordEncoder().username("Office").password("xxx!!").roles("OFFICE").build());
return new InMemoryUserDetailsManager(users);
}
}
I am using Postman to test my endpoints and it's working via the Authorization function of Postman.
What if I want to use this parameters (username,password,role) in my frontend?
I mean, that I am trying somehow to see how spring security passes the values to the url: http://localhost:8080 but i cant find out how. I need this to be able to handle in my Frontend with these vaules.
I am really new in this Spring Security Chapter.

Related

Reason for #EnableWebSecurity in the configuration class

I just read answer from the another question What is the use of #EnableWebSecurity in Spring?, but i couldn't understand why we need to add #EnableWebSecurity annotation at all.
Even I remove the #EnableWebSecurity from the configuration, my application still works.
Let's assume that we are going to implement either JWT based (rest api) or simply login based mvc application. For the following configuration what i am missing?
#Component
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public UserDetailsService userDetailsService() {
return new MyCustomUserDetailsService();
}
#Bean
public PasswsordEncoder passwsordEncoder() {
return new BrcyptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// for the jwt authentication add jwt filter etc ..
// for the login based, override default login page, error page etc..
}
}
If you are not using spring-boot but just a pure spring project , you definitely need to add #EnableWebSecurity in order to enable spring-security.
But if you are using spring-boot 2.0 +, you do not need to add it by yourself because the spring-boot auto configuration will automatically do it for you if you forget to do so. Under the cover , it is done by the WebSecurityEnablerConfiguration which its javadoc also states this behaviour as follows:
If there is a bean of type WebSecurityConfigurerAdapter, this adds the
#EnableWebSecurity annotation. This will
make sure that the annotation is present with default security
auto-configuration and also if the user adds custom security and
forgets to add the annotation.

Multi Tenancy with Spring Security OAuth2 Client

I am using Spring Security Oauth2 Client and Keycloak as Identity provider.
My application will be deployed with multiple domain and we want to use single instance of Keycloak.
I have set up 2 realms in a single instance of Keycloak treating them as different tenants.
In the application.properties I have set the properties for two tenants -
But how come the Application 1 with URL - http://demo-app-1.com will redirect to keycloak 1 and similarly for Application 2 with URL - http://demo-app-2.com will redirect to keycloak 2.
server.port=8300
spring.security.oauth2.client.registration.demo1.client-name=spring-boot-web
spring.security.oauth2.client.registration.demo1.client-id=spring-boot-web
spring.security.oauth2.client.registration.demo1.client-secret=213e66d5-206f-4948-bd9d-bfa14a70c4cf
spring.security.oauth2.client.registration.demo1.provider=keycloak
spring.security.oauth2.client.registration.demo1.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.demo1.redirect-uri=http://localhost:8300
spring.security.oauth2.client.provider.keycloak.authorization-uri=http://localhost:8081/auth/realms/spring-boot/protocol/openid-connect/auth
spring.security.oauth2.client.provider.keycloak.token-uri=http://localhost:8081/auth/realms/spring-boot/protocol/openid-connect/token
spring.security.oauth2.client.registration.demo2.client-name=spring-boot-web
spring.security.oauth2.client.registration.demo2.client-id=spring-boot-web
spring.security.oauth2.client.registration.demo2.client-secret=d69a7fd1-2297-49d0-b236-7b8039c845b2
spring.security.oauth2.client.registration.demo2.provider=keycloak2
spring.security.oauth2.client.registration.demo2.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.demo2.redirect-uri=http://localhost:8301
spring.security.oauth2.client.provider.keycloak2.authorization-uri=http://localhost:8081/auth/realms/spring-boot-2/protocol/openid-connect/auth
spring.security.oauth2.client.provider.keycloak2.token-uri=http://localhost:8081/auth/realms/spring-boot-2/protocol/openid-connect/token
Query - Is there any additional property which we can set which can auto route the requests to the respective realm in Keycloak?
I am getting a page to choose the provider when I hit the application URL which I need to bypass
Here is the Example For Opaque Token (Multitenant Configuration) maybe this is helpful - The Key for Multitenancy in Spring Security is Authentication Manger Resolver
#Component public class CustomAuthenticationManagerResolver implements AuthenticationManagerResolver {
#Override
public AuthenticationManager resolve(HttpServletRequest request) {
String tenantId = request.getHeader("tenant");
OpaqueTokenIntrospector opaqueTokenIntrospector;
if (tenantId.equals("1")) {
opaqueTokenIntrospector = new NimbusOpaqueTokenIntrospector(
"https://test/authorize/oauth2/introspect",
"clientId",
"clientSecret"
);
} else {
opaqueTokenIntrospector = new NimbusOpaqueTokenIntrospector(
"https://test/authorize/oauth2/introspect",
"clientId",
"clientSecret");
}
return new OpaqueTokenAuthenticationProvider(opaqueTokenIntrospector)::authenticate;
}
}
Web Security Configuration
#Autowired
private CustomAuthenticationManagerResolver customAuthenticationManagerResolver;
#Override
public void configure(HttpSecurity http) throws Exception {
http.anyRequest()
.authenticated().and().oauth2ResourceServer()
.authenticationEntryPoint(restEntryPoint).authenticationManagerResolver(customAuthenticationManagerResolver);
}

Spring: forwarding to /oauth/token endpoint loses authentication

I'm building a Spring Boot authorization server which needs to generate Oauth2 tokens with two different auth methods. I want to have a different endpoint for each method, but by default Spring only creates /oauth/token, and while it can be changed, I don't think it is possible to have two different paths for it.
As an alternative, I'm trying to create two methods in a controller which do an internal forward to /oauth/token, adding a parameter to the request so I can know where it came from.
I have something like this:
#RequestMapping(value = "/foo/oauth/token", method = RequestMethod.POST)
public ModelAndView fooOauth(ModelMap model) {
model.addAttribute("method", "foo");
return new ModelAndView("forward:/oauth/token", model);
}
This performs the forward correctly, but the auth fails with:
There is no client authentication. Try adding an appropriate authentication filter.
The same request works correctly when sent to /oauth/token directly, so I'm guessing that the problem is that the BasicAuthenticationFilter is not running after the forward.
How can I make it work?
I had exactly the same issue. After some research I found out that the problem was caused by Spring Boot 2, not by Spring Security configurations. According to the Spring Boot 2.0 migration guide:
Spring Security and Spring Session filters are configured for ASYNC, ERROR, and REQUEST dispatcher types.
and the Spring Boot's SecurityFilterAutoConfiguration source code:
#Bean
#ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(
SecurityProperties securityProperties) {
if (securityProperties.getFilter().getDispatcherTypes() == null) {
return null;
}
return securityProperties.getFilter().getDispatcherTypes().stream()
.map((type) -> DispatcherType.valueOf(type.name())).collect(Collectors
.collectingAndThen(Collectors.toSet(), EnumSet::copyOf));
}
where the defaults for securityProperties.getFilter().getDispatcherTypes() are defined in SecurityProperties as:
private Set<DispatcherType> dispatcherTypes = new HashSet<>(
Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));
Thus by default, Spring Boot configures Spring Security so that its filters will not be applied to FORWARD requests (but only to ASYNC, ERROR and REQUEST), and therefore no security filter will be applied to authenticate the requests when forwarding them to /oauth/token.
The solution is simple. You can either add the following line to your application.properties in order to apply default filters to ALL forwarded requests
spring.security.filter.dispatcher-types=async,error,request,forward
or create your own custom filter chain with a path matcher and dispatcherType=FORWARD to only filter requests that are forwared to /oauth/token.
Looking carefully to the filter chains created for the Oauth endpoints, and for the forwarding controllers, it's easy to see that the latter are missing the BasicAuthenticationFilter, because they aren't authenticated, and auth isn't performed again after the forward.
To solve it, I created a new config like this:
#Configuration
public class ForwarderSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
#Autowired
private FooClientDetailsService fooClientDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
for (AuthorizationServerConfigurer configurerBit : configurers) configurerBit.configure(configurer);
http.apply(configurer);
http
.authorizeRequests()
.antMatchers("/foo/oauth/token").fullyAuthenticated()
.and()
.requestMatchers()
.antMatchers("/foo/oauth/token");
http.setSharedObject(ClientDetailsService.class, fooClientDetailsService);
}
}
This code mimics what Spring Oauth does behind the scenes (here), running identical filter chains with the same authentication options on both endpoints.
When the /oauth/token endpoint finally runs, it finds the auth results that it expects, and everything works.
Finally, if you want to run a different ClientDetailsService on two forwarding endpoints, you just have to create two configuration classes like this one, and replace the ClientDetailsService on the setSharedObject call in each of them. Note that for this, you'll have to set different #Order values in each class.

Using spring security annotations with keycloak

I'm just a beginner in Spring Security, but I would like to know is it possible to configure keycloak in a way that I can use #PreAuthorize, #PostAuthorize, #Secured and other annotations.
For example, I've configured the keycloak-spring-security-adapter and Spring Security in my simple Spring Rest webapp so that I have access to Principal object in my controller, like this:
#RestController
public class TMSRestController {
#RequestMapping("/greeting")
public Greeting greeting(Principal principal, #RequestParam(value="name") String name) {
return new Greeting(String.format(template, name));
}
...
}
But when I try this (just an example, actually I want to execute custom EL expression before authorization):
#RestController
public class TMSRestController {
#RequestMapping("/greeting")
#PreAuthorize("hasRole('ADMIN')")
public Greeting greeting(Principal principal, #RequestParam(value="name") String name) {
return new Greeting(String.format(template, name));
}
...
}
I get exception:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
In my spring security config I enabled global method security:
What do I need to make this spring security annotations work? Is it possible to use this annotation in this context at all?
here is example code:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
#ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
}
and
#PreAuthorize("hasRole('ROLE_ADMIN')")
Apart from this code. you need to do the role mapping for realm roles and client(application roles). the application roles will be put in #PreAuthorize
You still have to configure Spring Security using Keycloak. Take a look at the adapter documentation for an annotation based configuration. Once that's set up your Spring Security annotations will work on authorized calls.

Add interceptor to spring boot mongodb rest example

I am trying to add an interceptor to a simple Spring-boot-mongodb-rest app, as can be seen here : http://spring.io/guides/gs/accessing-mongodb-data-rest/, in order to perform certain actions after the default rest handler is invoked. Here is my MongoRepository, whose CRUD operation is called upon a POST request to the server:
#RepositoryRestResource(collectionResourceRel = "reminder", path = "reminder")
public interface ReminderRepository extends MongoRepository<Reminder, String> {
List<Reminder> findBySendee(#Param("sendee") String sendee);
}
I am trying to register an interceptor for all HTTP requests by extending the WebMvcConfigurerAdapter class like this:
#Configuration
#ComponentScan
public class RemindxWebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new RemindxInterceptor());
}
}
As mentioned in the spring boot docs, I have not added the #EnableWebMvc annotation to this. While running the application, the addInterceptors function does get called and adds the interceptor. However, the given interceptor is not called after the POST handler is invoked. I am unable to figure out a way to have spring use this RemindxWebConfig for all MongoRepository http requests. Any inputs are appreciated.

Resources