Spring5 + Rest In Agile (2)
In day 1, we implemented a simple rest api with spring security enabled. Continue to add JWT feature for our rest api.
Setup Authorization Server
The idea of “Authorization Server” is to generate the token per access. The “Resource Server” will verify the token when accessing rest api.
We just hard code everything to make this simple.
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
static final String CLIEN_ID = "lzheng-client";
static final String CLIENT_SECRET = "lzheng-secret";
static final String GRANT_TYPE_PASSWORD = "password";
static final String AUTHORIZATION_CODE = "authorization_code";
static final String REFRESH_TOKEN = "refresh_token";
static final String IMPLICIT = "implicit";
static final String SCOPE_READ = "read";
static final String SCOPE_WRITE = "write";
static final String TRUST = "trust";
static final int ACCESS_TOKEN_VALIDITY_SECONDS = 1*60*60;
static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 6*60*60;
@Autowired
private TokenStore tokenStore;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer
.inMemory()
.withClient(CLIEN_ID)
.secret(CLIENT_SECRET)
.authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT )
.scopes(SCOPE_READ, SCOPE_WRITE, TRUST)
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS).
refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).accessTokenConverter(accessToke nConverter())
.authenticationManager(authenticationManager);
}
@Bean
public TokenStore buildJokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("abc");
return converter;
}
And get errror:
No qualifying bean of type 'org.springframework.security.authentication.AuthenticationManager'
Go back WebSecurityConfig file, and expose authnticationManager as Bean.
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Then get jaxb-api related issue due to java 9 remove all jaxb stuff from JDK
<!--fix for java 9+ -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>
Run unit test, we get green light again.
The next step is just to add unit test for jwt token
@Test
public void getTokenWithBasicAuth() {
// $ curl <client-id> : <client-secret>@<api-end-point-url >/oauth/token
// -d grant_type=password -d username=<username> -d password=< password >
ResponseEntity<String> resp = this.restTemplate.withBasicAuth("lzheng-client", "lzheng-secret")
.exchange(OAUTH_END_POINT, HttpMethod.POST, getOAuthFormData(), String.class);
Assert.assertEquals(HttpStatus.OK, resp.getStatusCode());
Assert.assertTrue("contain access_token string", resp.getBody().contains("access_token"));
}
@Test
public void getTokenWithoutBasicAuth() {
ResponseEntity<String> resp = this.restTemplate.withBasicAuth("", "")
.exchange(OAUTH_END_POINT, HttpMethod.POST, getOAuthFormData(), String.class );
Assert.assertEquals(HttpStatus.UNAUTHORIZED, resp.getStatusCode());
}
private HttpEntity<MultiValueMap<String, String>> getOAuthFormData() {
MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
map.add("grant_type", "password");
map.add("username", "user");
map.add("password", "password");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
return new HttpEntity<>(map, headers);
}
Set MULTEPART_FROM_DATA in the request header must be set to make the call correctly.
Don’t forget to run unit test to keep green light.
ResourceServerConfig
All above had setup the webconfig and oauth server config, the following tests should be passed.
- Access api/demo/hello with basic http (which is confined in websecurityConfig).
- Access oauth/token to get token
ResourceServerConfig will just add new authentication to enable jwt token access in this example.
@Configuration
@EnableResourceServer
/*@EnableResourceServer enables a Spring Security filter that authenticates requests using an incoming OAuth2 token.*/
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
/*class ResourceServerConfigurerAdapter implements ResourceServerConfigurer providing methods to adjust the access rules and paths that are protected by OAuth2 security.*/
private static final String RESOURCE_ID = "my_rest_api";
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.
anonymous().disable()
.requestMatchers().antMatchers("/api/**")
.and().authorizeRequests()
.antMatchers("/api/**").authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
And put the test code:
@Test
public void callHelooWithOAuth() {
OAuth2RestTemplate template = restTemplate();
ResponseEntity<String> resp = template
.exchange(covertToAbsulateUri(API_DEMO_HELLO), HttpMethod.GET, getOAuthFormData(), String.class );
Assert.assertEquals(HttpStatus.OK, resp.getStatusCode());
Assert.assertEquals(HELLO_API_EXPECTED, resp.getBody());
}
private String covertToAbsulateUri(String apiDemoHello) {
return "http://localhost:" + randomPort + apiDemoHello;
}
private OAuth2RestTemplate restTemplate() {
ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
resourceDetails.setGrantType("password");
resourceDetails.setAccessTokenUri(covertToAbsulateUri(OAUTH_END_POINT));
//-- set the clients info
resourceDetails.setClientId("lzheng-client");
resourceDetails.setClientSecret("lzheng-secret");
// set scopes
List<String> scopes = new ArrayList<>();
scopes.add("read");
scopes.add("write");
scopes.add("trust");
resourceDetails.setScope(scopes);
resourceDetails.setUsername("user");
resourceDetails.setPassword("password");
return new OAuth2RestTemplate(resourceDetails);
}
https://github.com/LeiZheng/spring-rest-jwt-agile
Comments
Post a Comment