zoukankan      html  css  js  c++  java
  • Using JWT with Spring Security OAuth

    http://www.baeldung.com/spring-security-oauth-jwt

    ****************************************************

    1. Overview

    In this tutorial we’ll discuss how to get our Spring Security OAuth2 implementation to make use of JSON Web Tokens.

    We’re also continuing to built on top of the previous article in this OAuth series.

    2. Maven Configuration

    First, we need to add spring-security-jwt dependency to our pom.xml:

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

    Note that we need to add spring-security-jwt dependency to both Authorization Server and Resource Server.

    3. Authorization Server

    Next, we will configure our 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() {
            return new JwtTokenStore(accessTokenConverter());
        }
     
        @Bean
        public JwtAccessTokenConverter accessTokenConverter() {
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            converter.setSigningKey("123");
            return converter;
        }
     
        @Bean
        @Primary
        public DefaultTokenServices tokenServices() {
            DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
            defaultTokenServices.setTokenStore(tokenStore());
            defaultTokenServices.setSupportRefreshToken(true);
            return defaultTokenServices;
        }
    }

    Note that we used a symmetric key in our JwtAccessTokenConverter to sign our tokens – which means we will need to use the same exact key for the Resources Server as well.

    4. Resource Server

    Now, let’s take a look at our Resource Server configuration – which is very similar to the config of the Authorization Server:

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

    Keep in mind that we’re defining these two servers as entirely separate and independently deployable. That’s the reason we need to declare some of the same beans again here, in the new configuration.

    5. Custom Claims in the Token

    Let’s now set up some infrastructure to be able to add a few custom claims in the Access Token. The standard claims provided by the framework are all well and good, but most of the time we’ll need some extra information in the token to utilize on the client side.

    We’ll define a TokenEnhancer to customize our Access Token with these additional claims.

    In the following example, we will add an extra field “organization” to our Access Token – with this CustomTokenEnhancer:

    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);
            return accessToken;
        }
    }

    Then, we’ll wire that into our 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() {
        return new CustomTokenEnhancer();
    }

    With this new configuration up and running – here’s what a token token payload would look like:

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

    5.1. Use the Access Token in the JS Client

    Finally, we’ll want to make use of the token information over in our AngualrJS client application. We’ll use the angular-jwt library for that.

    So what we’re going to do is we’re going to make use of the “organization” claim in our 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");
            var payload = jwtHelper.decodeToken(token);
            $scope.organization = payload.organization;
        }
        ...
    });

    6. Asymmetric KeyPair

    In our previous configuration we used symmetric keys to sign our token:

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

    We can also use asymmetric keys (Public and Private keys) to do the signing process.

    6.1. Generate JKS Java KeyStore File

    Let’s first generate the keys – and more specifically a .jks file – using the command line tool keytool:

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

    The command will generate a file called mytest.jks which contains our keys -the Public and Private keys.

    Also make sure keypass and storepass are the same.

    6.2. Export Public Key

    Next, we need to export our Public key from generated JKS, we can use the following command to do so:

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

    A sample response will look 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-----

    We take only our Public key and copy it to our resource server src/main/resources/public.txt:

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

    6.3. Maven Configuration

    Next, we don’t want the JKS file to be picked up by the maven filtering process – so we’ll make sure to exclude it in the pom.xml:

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

    If we’re using Spring Boot, we need to make sure that our JKS file is added to application 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>

    6.4. Authorization Server

    Now, we will configure JwtAccessTokenConverter to use our KeyPair from mytest.jks – as follows:

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

    6.5. Resource Server

    Finally, we need to configure our resource server to use Public key – as follows:

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        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;
    }

    7. Conclusion

    In this quick article we focused on setting up our Spring Security OAuth2 project to use JSON Web Tokens.

    The full implementation of this tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

  • 相关阅读:
    对象与内存控制1---实例变量和类变量
    数组与内存控制2--数组使用
    数组与内存控制1--数组初始化
    Java 三大特征之--多态
    简述Java面向对象三大特征:封装、继承、多态
    java程序初始化的顺序
    关于public static void main(String[] args)相关知识
    Java的优点
    前端面试攻略3------HTML和CSS部分
    前端面试攻略2------计算机网络部分
  • 原文地址:https://www.cnblogs.com/zhao1949/p/6639766.html
Copyright © 2011-2022 走看看