Skip to main content

Spring5 + Rest In Agile (2)


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.
  1. Access api/demo/hello with basic http (which is confined in websecurityConfig).
  2. 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

Popular posts from this blog

How to fix "ValueError when trying to compile python module with VC Express"

When I tried to compile the python, I always get compile issue as following: ------------ ... File "C:\Python26\lib\ distutils\msvc9compiler.py ", line 358, in initialize vc_env = query_vcvarsall(VERSION, plat_spec) File "C:\Python26\lib\ distutils\msvc9compiler.py ", line 274, in query_vcvarsall raise ValueError(str(list(result.keys()))) ValueError: [u'path'] --------------------- Python community discussed a lot but no solution: http://bugs.python.org/issue7511 The root cause is because the latest visual studio change the *.bat file a lot especially on 64bit env. The python 2.7 didn't update the path accordingly. Based on the assumption above, the following solution worked for me. To install Visual Studio 2008 Express Edition with all required components: 1. Install Microsoft Visual Studio 2008 Express Edition. The main Visual Studio 2008 Express installer is available from (the C++ installer name is vcsetup.exe): https://ww

How to convert the ResultSet to Stream

Java 8 provided the Stream family and easy operation of it. The way of pipeline usage made the code clear and smart. However, ResultSet is still go with very legacy way to process. Per actual ResultSet usage, it is really helpful if converted as Stream. Here is the simple usage of above: StreamUtils.uncheckedConsumer is required to convert the the SQLException to runtimeException to make the Lamda clear.

Interview for System Design 1: Designing a URL Shortening service like TinyURL.

Problem:  This service will provide short aliases redirecting to long URLs. Step 1: Requirement Analysis Understand the the basic core features: 1. create short url from long url. 2. get the long url from  the short url.  Nice to have feature: 3. will url get expired in certain time? 4. could user define their customized short url? here is some questions need to clarify:  1. How long we need keep the url?  (it will have impact on storage, it is very import to understand to how long will the data be if such data will be stored in local storage). 2. Do we allow N : 1 or only 1: 1 mapping? (have impact about algorithm and data storage.  Step 2:   Estimation Of  Resource Usage common resources: data storage || web services: QPS Let's the estimation right now:  Assume DAU is about 500M,  Create: and one user will create new one item every 5 days. so the total creation per Second will be a. yearly new record: 500M/5 * 365 ~ 50G, new records a. monthly storage: 500M/5 * 100  * 30 = 100M *