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);
}
Related
I have a set of services behind a zuul gateway, one of which is the auth server. I have it configured to parse the jwt with a jwk set from the auth server at /.well-known/jwks.json for users on each service with a password grant to access simple endpoints, but i'm wondering if it's possible to decide on a case by case basis which controllers and endpoints are using the user's access token vs using the service's client credentials when those services have to call other services. for example:
I have a contact service that manages customers, and another service that manages inventory.
When a user wants to see which customers are interacting with which inventory, i'm able to use an OAuth2RestTemplate to call the other service like so
#RequestMapping("/sales/{id}")
public Map<Object, Customer> getSales(#PathVariable Long customerId) {
Object inventory = restTemplate.getForObject("http://inventory-service/inventory", Object.class);
Customer customer = repository.findById(customerId);
Map<Object, Customer> sales = new HashMap;
sales.put(customer, inventory);
return sales;
}
I'm getting a 500 response A redirect is required to get the users approval even though i've tried configuring the Customer service to use client credentials flow instead of authorization code flow.
the security settings for the customer service:
security:
oauth2:
resource:
jwk:
key-set-uri: ${GATEWAY:http://localhost:8762}/.well-known/jwks.json
client:
client-id: first-client
client-secret: noonewilleverguess
access-token-uri: ${GATEWAY:http://localhost:8762}/oauth/token
with the main class annotated with #SpringBootApplication, #EnableGlobalMethodSecurity(prePostEnabled = true), and #EnableResourceServer.
here's some more configuration for context
#EnableWebSecurity
public class SecurityConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}
#LoadBalanced
#Bean
public OAuth2RestTemplate restTemplate(OAuth2ClientContext clientContext,
OAuth2ProtectedResourceDetails resourceDetails) {
return new OAuth2RestTemplate(resourceDetails, clientContext);
}
the documentation suggests that #EnableOAuth2Client isn't necessary when exposing the OAuth2RestTemplate so i have omitted that annotation.
Ideally i'd like to be able to pick and choose which requests use the user's access token and which requests use the service's client credentials, but i haven't found any resources that do so. Is it even possible?
I am having trouble adding custom permissions from our local database to my Spring Security Principal object.
My goals for our security is to do the following:
Authenticate with OAuth2 from our company (done)
Query our local database and gather user permissions (the query is written)
Add this list of permissions to my principal object for use throughout the application
I would prefer if this is done right when the user logs in using the configure() method in Spring Security configuration.
In more simple terms, I want to say "Hey, now that the user is logged in, take that username and plug it into this query. Take the results of that query, and add them to my principal object so I can use it anywhere in the application"
Here is my very simple security configuration :
#Configuration
#EnableOAuth2Sso
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http)
throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
Here is an example from my rest controller of gathering user information from my principal object:
#RestController
#RequestMapping("user")
#Slf4j
public class UserController {
#GetMapping(value = "/authentication")
public Authentication getAuth(Principal principal){
OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) principal;
Authentication authentication = oAuth2Authentication.getUserAuthentication();
Map<String, String> details = new LinkedHashMap<>();
details = (Map<String, String>) authentication.getDetails();
return authentication;
}
}
This works great, so I have the users email address, first name, last name, etc... that my company provides from their OAuth2 token. I just need to modify this principal object to add permissions... right when the user logs in...
Expected results:
I should have an principal object that has all the user information like email, first name, last name that my company provides from OAuth. Also, the principal object should have a list of permissions that we gathered from a custom database query.
I am new to spring session and spring security. I am developing an e-commerce application. My application will not have logged in user.So whenever a user lands on my application I need to create a session to this user and whenever the user adds item to the cart it should store in session.On Checkout i will store this cart items into the database.I could acheive this functionality by servlets using HTTPsession but as per my knowledge using httpsession is not good practice.So i am planning to implement it in spring session and spring security.My question is i dont have authenticated user so is it possible for spring security to create session for anonymous user.I am using HeaderHttpSessionStrategy
For example i am calling "localhost:8080/token" from my app which creates a session id and send to my client.i store the response session id in my localstorage and planning to send this sessionid as X-Auth-token to my headers from next request.If i do so i dont have any authentication configured in my security config as i dont have logged in user.While implementing my cart do i need to get this request header and store it in session so that session is maintained and cart is saved and retreived from session. Will this create me new session for each new user ?.Or else if i dont have logged in user is it ok to implement HttpSession using servlet?. Please let me know with this code whether my session is stored in redis database or not?Please suggest me way or sample to implement my functionality in efficient way.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
private static final String[] PUBLIC_MATCHERS = { "/css/**", "/js/**", "/image/**", "/book/**", "/user/**", "/**" };
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().disable().httpBasic().and().authorizeRequests().antMatchers(PUBLIC_MATCHERS)
.permitAll().anyRequest().anonymous();
}
#Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy();
}
}
HttpSessionConfig.java
#EnableRedisHttpSession
public class HttpSessionConfig {
#Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}
I am currently working on building an Angular2-Application accessing a Spring Boot backend server for authentication and editing database data via REST-APIs. My current basic configuration to authenticate against our Active Directory looks like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin();
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).userDetailsService(userDetailsService());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(AD_DOMAIN, "ldap://" + AD_URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
This works so that I can access the basic Spring Boot Actuator-APIs after logging in using the default Boot Login Form.
The next step would be intertwining the activeDirectoryLdapAuthenticationProvider with a token-based authentication solution that can be accessed by Angular2 - for that, i found an example repository on gitHub. It implements HMAC-based authentication using JWT tokens with a custom service structure.
My issue now comes in understanding how the components of Spring Security work together in the background. As i understand the example, it implements a customised version of UserDetailsService and its environment using the UserDTO and LoginDTO. These access a MockUser-Service for example user data - which is what I want to avoid, instead accessing our Active Directory to authenticate users.
I am still digging through the repository trying to understand every customised class implementation and if I can customise the AuthenticationService to fit my needs, but I feel relatively lost.
What I am looking for are experiences in combining these three components, or with Spring Security and how exactly the default UserDetailsService and AuthenticationProviders interact, so I can try and fit the ActiveDirectory-logic into the token solution.
Alternatively, is there a way to combine the default-LDAP/AD-Configuration with a default JWT-Solution such as this?
I'm in the process of breaking apart a monolith into microservices. The new microservices are being written with Spring using Spring Security and OAuth2. The monolith uses its own custom security that is not spring security, and for now the users will still be logging into the monolith using this homegrown security. The idea is that the new MS apps will have their own user base, and the monolith app itself will be a "user" of these Services. I've successfully set up an OAuth2 Auth Server to get this working and I'm able to log in with Client Credentials to access the REST APIs.
The problem is that the Microservices also include their own UIs which will need to be accessed both directly by admins (using the new Microservice users and a login page) and through the monolith (hopefully using client credentials so that the monolith users do not have to log in a second time). I have the first of these working, I can access the new UIs, I hit the login page on the OAuth server, and then I'm redirected back to the new UIs and authenticated & authorized.
My expectation from the is that I can log in to the OAuth server with the client credentials behind the scenes and then use the auth token to have the front end users already authenticated on the front end.
My question is - what should I be looking at to implement to get the client credentials login to bypass the login page when coming in through the UI? Using Postman, I've gone to http://myauthapp/oauth/token with the credentials and gotten an access token. Then, I thought I could perhaps just GET the protected UI url (http://mymicroservice/ui) with the header "Authorization: Bearer " and I was still redirected to the login page.
On the UI app:
#Configuration
#EnableOAuth2Client
protected static class ResourceConfiguration {
#Bean
public OAuth2ProtectedResourceDetails secure() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setId("secure/ui");
details.setClientId("acme");
details.setClientSecret("acmesecret");
details.setAccessTokenUri("http://myoauthserver/secure/oauth/token");
details.setUserAuthorizationUri("http://myoauthserver/secure/oauth/authorize");
details.setScope(Arrays.asList("read", "write"));
details.setAuthenticationScheme(AuthenticationScheme.query);
details.setClientAuthenticationScheme(AuthenticationScheme.form);
return details;
}
#Bean
public OAuth2RestTemplate secureRestTemplate(OAuth2ClientContext clientContext) {
OAuth2RestTemplate template = new OAuth2RestTemplate(secure(), clientContext);
AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(
Arrays.<AccessTokenProvider> asList(
new AuthorizationCodeAccessTokenProvider(),
new ResourceOwnerPasswordAccessTokenProvider(),
new ClientCredentialsAccessTokenProvider())
);
template.setAccessTokenProvider(accessTokenProvider);
return template;
}
}
SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private OAuth2ClientContextFilter oAuth2ClientContextFilter;
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.anonymous().disable()
.csrf().disable()
.authorizeRequests()
.antMatchers("/ui").hasRole("USER")
.and()
.httpBasic()
.authenticationEntryPoint(oauth2AuthenticationEntryPoint());
}
private LoginUrlAuthenticationEntryPoint oauth2AuthenticationEntryPoint() {
return new LoginUrlAuthenticationEntryPoint("/login");
}
}