Dave Syer, 2014
Twitter: @david_syer
Email: dsyer@pivotal.io
(Security for Microservices with Spring)
Stop bad guys from accessing your resources
Identity and permissions:
Example:
$ curl "https://$username:$password@myhost/resource"
@Grab('spring-boot-starter-security')
@RestController
class Application {
@RequestMapping("/")
def home() {
[status: 'OK']
}
}
Authentication
Example:
$ curl -k --cert rod.pem:password https://localhost:8443/hello
https://github.com/SpringOne2GX-2014/microservice-security/tree/master/certs
AbstractPreAuthenticatedProcessingFilter
and friendshttps://github.com/SpringOne2GX-2014/microservice-security/tree/master/pairs/spring-session
Example command line Client:
$ curl -H "Authorization: Bearer $TOKEN" https://myhost/resource
https://myhost
is a Resource ServerTOKEN
is a Bearer Token@EnableResourceServer
class ResourceServer {
@Bean
JwtTokenStore tokenStore() throws Exception {
JwtAccessTokenConverter enhancer =
new JwtAccessTokenConverter()
enhancer.afterPropertiesSet()
new JwtTokenStore(enhancer)
}
}
N.B. in a real system you would have to configure the verifierKey (or use JdbcTokenStore)
@EnableAuthorizationServer
class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Override
void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("my-client-with-secret")...
}
}
Example of implementation (from Cloud Foundry UAA, JWT = signed, base64-encoded, JSON):
{ "client_id":"cf",
"exp":1346325625,
"scope":["cloud_controller.read","openid","password.write"],
"aud":["openid","cloud_controller","password"],
"iss": "https://login.run.pivotal.io",
"user_name":"tester@vmware.com",
"user_id":"52147673-9d60-4674-a6d9-225b94d7a64e",
"email":"tester@vmware.com",
"jti":"f724ae9a-7c6f-41f2-9c4a-526cea84e614" }
A further level of abstraction to make common microservice security use cases really easy to implement
@EnableOAuth2Sso
@Controller
class Demo {
}
$ spring jar app.jar app.groovy
$ cf push -p app.jar
(That's it.)
Answer: configuration conventions. The app was bound to a service
$ cf bind-service app sso
and the service provides credentials.
To create the same bindings manually (e.g. in application.yml
):
oauth2:
client:
tokenUri: https://login.run.pivotal.io/oauth/token
authorizationUri: https://login.run.pivotal.io/oauth/authorize
clientId: acme
clientSecret: ${CLIENT_SECRET}
resource:
tokenInfoUri: http://uaa.run.pivotal.io/check_token
id: openid
serviceId: ${PREFIX:}resource
@EnableOAuth2Resource
@EnableEurekaClient
@RestController
class Demo {
@RequestMapping("/")
def home() { [id: UUID.randomUUID().toString(), content: "Hello Remote"] }
}
How does it work? Same as @EnableOAuth2Sso
(bind to service
providing credentials for conventional external configuration).
With backend services CORS restrictions make reverse proxy useful (@EnableZuulProxy
).
Then you can acquire tokens in the client app and relay them to back end.
With no backend services, don't be shy, use the session (authorization code flow is vastly superior).
Spring Session helps a lot too.
Front end app sends SSO token with user credentials to authenticate back end requests, back ends just relay it to each other as necessary.
Simple but possibly flawed: the front end only needs access to user details to authenticate, but you need to give it permission to do other things to allow it access to the back ends.
Idea: exchange (with full authentication) the incoming token for an outgoing one with different permissions (client but not scope). Can use password grant (e.g. with the incoming token as a password).
@EnableOAuth2Sso
@EnableZuulProxy
@Controller
class Demo {
}
@EnableZuulProxy
combined with @EnableOAuth2Sso
ZuulFilter
that attaches the current user token to downstream requests/
#