-
part of microservices demo
-
use with:
-
token management within oauth protected app
-
capabilities:
- create token
- validate token
- refresh token
- Add
@EnableAuthorizationServer
annotation- will support with a number of defaults for the oauth authorization server
- will expose a number of endpoints that can be use to receive, validate or refresh a token
- Create a configuration class (
AuthorizationServerConfig.java
) that extendsAuthorizationServerConfigurerAdapter
that is going to facilitate the configuration of the project- add
@Configuration
annotation to new class so spring's IoC container will pick up this class and begin building beans - inject an
AuthenticationManager
because this demo is using password grant that is not enabled by default within Spring Cloud support for Oauth - override
configure(AuthorizationServerEndpointsConfigurer endpoints)
method and injectAuthenticationManager
bean@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); }
- override
configure(ClientDetailsServiceConfigurer clients)
method that will allow to set up the oauth client that is need available within the application or within the authorization server (for this demo it will be in memory)@Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // establish a config for a web app client in the auth server config clients.inMemory() .withClient("webapp") .secret("{noop}secret") // to avoid There is no PasswordEncoder mapped for the id \"null\" .authorizedGrantTypes("password") // it is how user authenticate with the auth server .scopes("read,write,trust") // determine the access level to the different pieces of the API }
- add
- Create a web security config class (
WebSecurityConfig.java
) that extendsWebSecurityConfigurerAdapter.class
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } // allow to specify the user within the web application @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("user1") .password("{noop}password") // to avoid There is no PasswordEncoder mapped for the id \"null\" .roles("USER") ; } }
- Test with Postman
- POST method
- URL: http://localhost:9090/oauth/token?grant_type=password&username=user1&password=password
- QUERY STRING PARAMS: are according the oauth specification that denotes how we exchange the different calls
between the different pieces of the architecture within oauth security in order for them to follow specific
process to grant users access to some resource
grant_type
: password (see AuthorizationServerConfig.java) - oauth2 specusername
password
- QUERY STRING PARAMS: are according the oauth specification that denotes how we exchange the different calls
between the different pieces of the architecture within oauth security in order for them to follow specific
process to grant users access to some resource
- Authentication: Basic
- username: webapp
- password: secret
- Expected response:
{ "access_token": "1355ede7-3a32-43df-9fb0-75883bab0636", "token_type": "bearer", "expires_in": 43199, "scope": "read,write,trust" }
-
a request agains the authorization server is made to get an access token
"1355ede7-3a32-43df-9fb0-75883bab0636"
and that access token can be used to gain access to resources that are protected by the security architecture -
reference:
-
NOTE resource server is implemented separated from authorization server here
-
is the underlaying API server that is used to access user information
-
using a token from the authorization server allow to obtain acces to protected resources located in the rest API
-
to have the authorization server and resource server configuration in the same place is only applicable for the most trivial of applications
-
in order to establish the resource server we need:
- add
@EnableResourceServer
annotation - and must exist a resource that is going to be protected by the resource server i.e.
@RequestMapping("/resource/endpoint") public String endpoint() { return "resource protected by the resource server"; }
@RestController
allow to expose the resource API (with@RequestMapping
) and act as part of the Resource Server which works in tandem with the Auhorization Server to protect the resource
- add
-
Test
- when hit direct to endpoint
http://localhost:9090/resource/endpoint
without access token the client receive a401 Unauthorized
{ "error": "unauthorized", "error_description": "Full authentication is required to access this resource" }
- in order to grant access to the resource a token must be created and give that token to the request of the endpoint
- hit authorization server to create token described above in this document (authorization server config)
{ "access_token": "a4c6f9fd-7bf7-4823-9649-d1116f82df8e", "token_type": "bearer", "expires_in": 43199, "scope": "read,write,trust" }
- hit resource endpoint with token created above
http://localhost:9090/resource/endpoint?access_token=a4c6f9fd-7bf7-4823-9649-d1116f82df8e
resource protected by the resource server
access_token
: is accepted by the resource server due to the relationship with authorization server within the authentication scheme
- when hit direct to endpoint
-
Protect API endpoints with spring security
method security annotations
- add
@EnableGlobalMethodSecurity
annotation using theprePostEnabled
set to true - add
@PreAuthorize("hasRole('ADMIN')")
method annotation to discerning who is able to invoke this method in the expression the user needs to have the ADMIN role to access this method@RequestMapping("/resource/endpoint") @PreAuthorize("hasRole('ADMIN')") public String endpoint() { return "resource protected by the resource server"; }
- to test this configuration create an access token for user
admin
and then access to the annotated method if token is created with useruser
then resource server will not grant access
- add
-
database will store information about tokens that were issued by the authorization server
-
resource server will use the database to confirm the tokens received are valid and were issued by the authorization server
-
this test is with an
HSQLDB
- download hsqldb
- create
server.properties
in root directory of hsqldbserver.database.0=file:hsqldb/poc server.dbname.0=testdb server.port=9137
- build server from root directory of hsqldb
$java -classpath lib/hsqldb.jar org.hsqldb.server.Server
- start server
$java -classpath lib/hsqldb.jar org.hsqldb.server.Server --database.0 file:hsqldb/poc --dbname.0 testdb
- hsqldb GUI can be found in
bin/runManagerSwing
- select
type: hsql database engine server
and user port defined inserver.properties
- select
-
configure schema in spring boot creating
src/main/resources/schema.sql
schema.sql
is a special spring boot file name because it will pick the file and load any scripts within the file into the jdbc store (hsqldb) prior to starting the application- copy schema from spring-security-oauth2 github repository
- insert client details
-
add
configure(AuthorizationServerSecurityConfigurer security)
inAuthorizationServerConfig
file -
set the data source bean for the hsqldb server
@Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); dataSource.setUrl("jdbc:hsqldb:hsql://localhost:9137/testdb"); dataSource.setUsername("SA"); dataSource.setPassword(""); return dataSource; }
-
create a
TokenStore
object of typeJdbcTokenStore
injecting the data source bean -
edit body of
configure(ClientDetailsServiceConfigurer clients)
method@Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.jdbc(dataSource()); // to allow to connect to db and retrieve clients }
-
reference:
- Demo can be found here