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.

  • 相关阅读:
    原生JavaScript封装insertAfter方法
    SQL截取最后一个由字符分隔的字符串
    给标识列显示的添加数据(IDENTITY_INSERT 为 ON)
    C#保留两位小数,四舍五入的函数及使用方法
    UEditor1.4.3上传图片提示上传失败
    SQL Server 2005公用表表达式(CTE)
    UEditor 粘贴表格时报错导致无法粘贴的解决方法
    事件监听机制
    数组中的趣味题(一)
    redis的工具类封装
  • 原文地址:https://www.cnblogs.com/zhao1949/p/6639766.html
Copyright © 2011-2022 走看看