www.baeldung.com/spring-secu…

Eugen Paraschiv

From stackGC

1, an overview of the

In this tutorial, we will discuss Spring Security OAuth2 integration with JSON Web Token.

This article is based on the previous OAuth series.

2. Maven configuration

First, we need to add the spring-security-jWT dependency to pem.xml:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-jwt</artifactId>
</dependency>
Copy the code

Note that we need to add spring-security-JWT dependencies to the pm.xml for both the authorization server and the resource server.

3. Authorization server

Next, we will configure the authorization server to use JwtTokenStore as follows:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore())
                 .accessTokenConverter(accessTokenConverter())
                 .authenticationManager(authenticationManager);
    }
 
    @Bean
    public TokenStore tokenStore(a) {
        return new JwtTokenStore(accessTokenConverter());
    }
 
    @Bean
    public JwtAccessTokenConverter accessTokenConverter(a) {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");
        return converter;
    }
 
    @Bean
    @Primary
    public DefaultTokenServices tokenServices(a) {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        returndefaultTokenServices; }}Copy the code

Note that we use a symmetric key in JwtAccessTokenConverter to sign our token — meaning that the resource server needs to use the same key.

4. Resource server

Let’s look at the resource server configuration, which is very similar to the authorization server configuration:

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(ResourceServerSecurityConfigurer config) {
        config.tokenServices(tokenServices());
    }
 
    @Bean
    public TokenStore tokenStore(a) {
        return new JwtTokenStore(accessTokenConverter());
    }
 
    @Bean
    public JwtAccessTokenConverter accessTokenConverter(a) {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");
        return converter;
    }
 
    @Bean
    @Primary
    public DefaultTokenServices tokenServices(a) {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        returndefaultTokenServices; }}Copy the code

Keep in mind that the two servers are completely separate and deployable independently, which is why we declare some of the same beans again in the new configuration.

5. User-defined Claims in tokens

Next, set up some infrastructure so that you can add custom claims to Access tokens. The standard claims provided by the framework are good, but in most cases we need to add some extra information to the token to use it on the client side.

We define a TokenEnhancer to customize Access tokens and these additional claims.

In the following example, we will use this CustomTokenEnhancer to add an additional field organization to Access Toekn:

public class CustomTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance( OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        Map<String, Object> additionalInfo = new HashMap<>();
        additionalInfo.put("organization", authentication.getName() + randomAlphabetic(4));
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
        returnaccessToken; }}Copy the code

Then, assemble it into the authorization server configuration as follows:

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
    tokenEnhancerChain.setTokenEnhancers(
      Arrays.asList(tokenEnhancer(), accessTokenConverter()));
 
    endpoints.tokenStore(tokenStore())
             .tokenEnhancer(tokenEnhancerChain)
             .authenticationManager(authenticationManager);
}
 
@Bean
public TokenEnhancer tokenEnhancer(a) {
    return new CustomTokenEnhancer();
}
Copy the code

Using this new configuration to start and run, the payload of the token might look like this:

{
    "user_name": "john"."scope": [
        "foo"."read"."write"]."organization": "johnIiCh"."exp": 1458126622."authorities": [
        "ROLE_USER"]."jti": "e0ad1ef3-a8a5-4eef-998d-00b26bc2c53f"."client_id": "fooClientIdPassword"
}
Copy the code

5.1 Use Access Token in JS client

Finally, we will use the token information in the AngualrJS client application. The Angular-JWT library is used here.

Use organization claim in index.html:

<p class="navbar-text navbar-right">{{organization}}</p>
 
<script type="text/javascript"
  src="https://cdn.rawgit.com/auth0/angular-jwt/master/dist/angular-jwt.js">
</script>
 
<script>
var app = angular.module('myApp'["ngResource"."ngRoute"."ngCookies"."angular-jwt"]);
 
app.controller('mainCtrl'.function($scope, $cookies, jwtHelper,...). { $scope.organiztion = "";
 
    function getOrganization(){
        var token = $cookies.get("access_token");
        varpayload = jwtHelper.decodeToken(token); $scope.organization = payload.organization; }... });Copy the code

6. Asymmetric key pairs

In the previous configuration, we used the symmetric key to sign the token:

@Bean
public JwtAccessTokenConverter accessTokenConverter(a) {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setSigningKey("123");
    return converter;
}
Copy the code

We can also use asymmetric keys (public and private keys) to complete the signing process.

6.1. Generate the JKS Java KeyStore file

Start by using the command line tool keytool to generate the keys — in particular.jks files.

keytool -genkeypair -alias mytest 
                    -keyalg RSA 
                    -keypass mypass 
                    -keystore mytest.jks 
                    -storepass mypass
Copy the code

This command will generate a file named mytest.jks that contains our keys — public and private.

Also, make sure keypass and StorePass are the same.

6.2. Export the public key

Next, to export the public key from the generated JKS, use the following command:

keytool -list -rfc --keystore mytest.jks | openssl x509 -inform pem -pubkey
Copy the code

The output might look something like this:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgIK2Wt4x2EtDl41C7vfp
OsMquZMyOyteO2RsVeMLF/hXIeYvicKr0SQzVkodHEBCMiGXQDz5prijTq3RHPy2
/5WJBCYq7yHgTLvspMy6sivXN7NdYE7I5pXo/KHk4nz+Fa6P3L8+L90E/3qwf6j3
DKWnAgJFRY8AbSYXt1d5ELiIG1/gEqzC0fZmNhhfrBtxwWXrlpUDT0Kfvf0QVmPR
xxCLXT+tEe1seWGEqeOLL5vXRLqmzZcBe1RZ9kQQm43+a9Qn5icSRnDfTAesQ3Cr
lAWJKl2kcWU1HwJqw+dZRSZ1X4kEXNMyzPdPBbGmU6MHdhpywI7SKZT7mX4BDnUK
eQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN CERTIFICATE-----
MIIDCzCCAfOgAwIBAgIEGtZIUzANBgkqhkiG9w0BAQsFADA2MQswCQYDVQQGEwJ1
czELMAkGA1UECBMCY2ExCzAJBgNVBAcTAmxhMQ0wCwYDVQQDEwR0ZXN0MB4XDTE2
MDMxNTA4MTAzMFoXDTE2MDYxMzA4MTAzMFowNjELMAkGA1UEBhMCdXMxCzAJBgNV
BAgTAmNhMQswCQYDVQQHEwJsYTENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAICCtlreMdhLQ5eNQu736TrDKrmTMjsrXjtkbFXj
Cxf4VyHmL4nCq9EkM1ZKHRxAQjIhl0A8+aa4o06t0Rz8tv+ViQQmKu8h4Ey77KTM
urIr1zezXWBOyOaV6Pyh5OJ8/hWuj9y/Pi/dBP96sH+o9wylpwICRUWPAG0mF7dX
eRC4iBtf4BKswtH2ZjYYX6wbccFl65aVA09Cn739EFZj0ccQi10/rRHtbHlhhKnj
iy+b10S6ps2XAXtUWfZEEJuN/mvUJ+YnEkZw30wHrENwq5QFiSpdpHFlNR8CasPn
WUUmdV+JBFzTMsz3TwWxplOjB3YacsCO0imU+5l+AQ51CnkCAwEAAaMhMB8wHQYD
VR0OBBYEFOGefUBGquEX9Ujak34PyRskHk+WMA0GCSqGSIb3DQEBCwUAA4IBAQB3
1eLfNeq45yO1cXNl0C1IQLknP2WXg89AHEbKkUOA1ZKTOizNYJIHW5MYJU/zScu0
yBobhTDe5hDTsATMa9sN5CPOaLJwzpWV/ZC6WyhAWTfljzZC6d2rL3QYrSIRxmsp
/J1Vq9WkesQdShnEGy7GgRgJn4A8CKecHSzqyzXulQ7Zah6GoEUD+vjb+BheP4aN
hiYY1OuXD+HsdKeQqS+7eM5U7WW6dz2Q8mtFJ5qAxjY75T0pPrHwZMlJUhUZ+Q2V
FfweJEaoNB9w9McPe1cAiE+oeejZ0jq0el3/dJsx3rlVqZN+lMhRJJeVHFyeb3XF
lLFCUGhA7hxn2xf3x1JW
-----END CERTIFICATE-----
Copy the code

We use only the public key, copy it to the server resources of SRC/main/resources/public. TXT file:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgIK2Wt4x2EtDl41C7vfp
OsMquZMyOyteO2RsVeMLF/hXIeYvicKr0SQzVkodHEBCMiGXQDz5prijTq3RHPy2
/5WJBCYq7yHgTLvspMy6sivXN7NdYE7I5pXo/KHk4nz+Fa6P3L8+L90E/3qwf6j3
DKWnAgJFRY8AbSYXt1d5ELiIG1/gEqzC0fZmNhhfrBtxwWXrlpUDT0Kfvf0QVmPR
xxCLXT+tEe1seWGEqeOLL5vXRLqmzZcBe1RZ9kQQm43+a9Qn5icSRnDfTAesQ3Cr
lAWJKl2kcWU1HwJqw+dZRSZ1X4kEXNMyzPdPBbGmU6MHdhpywI7SKZT7mX4BDnUK
eQIDAQAB
-----END PUBLIC KEY-----
Copy the code

6.3. Maven Configuration

We don’t want the JKS file to be affected by Maven filtering, so we need to make sure we exclude it in POM.xml:

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <excludes>
                <exclude>*.jks</exclude>
            </excludes>
        </resource>
    </resources>
</build>
Copy the code

If you use Spring Boot, you can add the JKS file to your application’s classpath via the Spring Boot Maven plugin addResources:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <addResources>true</addResources>
            </configuration>
        </plugin>
    </plugins>
</build>
Copy the code

6.4. Authorization Server

Configure JwtAccessTokenConverter to use the key pair in Mytest.jks as follows:

@Bean
public JwtAccessTokenConverter accessTokenConverter(a) {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    KeyStoreKeyFactory keyStoreKeyFactory = 
      new KeyStoreKeyFactory(new ClassPathResource("mytest.jks"), "mypass".toCharArray());
    converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest"));
    return converter;
}
Copy the code

6.5 resource Server

Finally, you need to configure the resource server to use a public key, as follows:

@Bean
public JwtAccessTokenConverter accessTokenConverter(a) {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    Resource resource = new ClassPathResource("public.txt");
    String publicKey = null;
    try {
        publicKey = IOUtils.toString(resource.getInputStream());
    } catch (final IOException e) {
        throw new RuntimeException(e);
    }
    converter.setVerifierKey(publicKey);
    return converter;
}
Copy the code

7, the conclusion

This article describes how to configure a Spring Security OAuth2 project to integrate JSON Web tokens.

The full implementation of this tutorial can be found at github, an Eclipse project that is easy to import and run.

Sample code

Github.com/eugenp/spri…