oauth2 jwt token 返回accessToken对象中携带用户字段信息

oauth2 源码中用户信息只返回用户名称,现有需求要求返回userId,只能重写某些方法,server端和client端都要修改:

方法一、结果:userId 封装在token中,需要解析access_token才能看到,但是经测试发现,使用此方法之后refresh_token获取access_token时,自定义的字段都不见了,连自带的username也不见了,应该是要再重写refresh_token获取时的一些方法,暂未实现。

server:

1、重写 DefaultAccessTokenConverter.java的部分方法:

import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;

import java.util.HashMap;
import java.util.Map;

public class MyDefaultAccessTokenConverter extends DefaultAccessTokenConverter {

    private boolean includeGrantType;

    private String scopeAttribute = SCOPE;

    private String clientIdAttribute = CLIENT_ID;

    private MyUserAuthenticationConverter userTokenConverter = new MyUserAuthenticationConverter();

    public Map convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
        Map response = new HashMap();
        OAuth2Request clientToken = authentication.getOAuth2Request();

        if (!authentication.isClientOnly()) {
            response.putAll(userTokenConverter.convertUserAuthentication(authentication.getUserAuthentication()));
        } else {
            if (clientToken.getAuthorities()!=null && !clientToken.getAuthorities().isEmpty()) {
                response.put(MyUserAuthenticationConverter.AUTHORITIES,
                        AuthorityUtils.authorityListToSet(clientToken.getAuthorities()));
            }
        }

        if (token.getScope()!=null) {
            response.put(scopeAttribute, token.getScope());
        }
        if (token.getAdditionalInformation().containsKey(JTI)) {
            response.put(JTI, token.getAdditionalInformation().get(JTI));
        }

        if (token.getExpiration() != null) {
            response.put(EXP, token.getExpiration().getTime() / 1000);
        }

        if (includeGrantType && authentication.getOAuth2Request().getGrantType()!=null) {
            response.put(GRANT_TYPE, authentication.getOAuth2Request().getGrantType());
        }

        response.putAll(token.getAdditionalInformation());

        response.put(clientIdAttribute, clientToken.getClientId());
        if (clientToken.getResourceIds() != null && !clientToken.getResourceIds().isEmpty()) {
            response.put(AUD, clientToken.getResourceIds());
        }
        return response;
    }
}

2、重写  UserAuthenticationConverter.java的部分方法:

import com.langyatech.fcp.oauth.entity.OauthUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;

import java.util.LinkedHashMap;
import java.util.Map;

public class MyUserAuthenticationConverter implements UserAuthenticationConverter {

    @Override
    public Map convertUserAuthentication(Authentication authentication) {
        LinkedHashMap response = new LinkedHashMap();
        response.put("userId", ((OauthUser) authentication.getPrincipal()).getUserId());
        if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
            response.put("authorities", AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
        }
        return response;
    }

    @Override
    public org.springframework.security.core.Authentication extractAuthentication(Map map) {
        return null;
    }
}

3、重写 JwtAccessTokenConverter.java

import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.*;
import org.springframework.security.oauth2.common.*;
import org.springframework.security.oauth2.common.util.JsonParser;
import org.springframework.security.oauth2.common.util.JsonParserFactory;
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import java.util.Map;

public class MyJwtAccessTokenConverter extends JwtAccessTokenConverter
{

    private AccessTokenConverter tokenConverter = new MyDefaultAccessTokenConverter();

    private JsonParser objectMapper = JsonParserFactory.create();

    private String verifierKey = new RandomValueStringGenerator().generate();

    private Signer signer = new MacSigner(verifierKey);


    /**
     * @param tokenConverter the tokenConverter to set
     */
    @Override
    public void setAccessTokenConverter(AccessTokenConverter tokenConverter) {
        this.tokenConverter = tokenConverter;
    }

    /**
     * @return the tokenConverter in use
     */
    @Override
    public AccessTokenConverter getAccessTokenConverter() {
        return tokenConverter;
    }

    @Override
    public Map convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
        return tokenConverter.convertAccessToken(token, authentication);
    }

    @Override
    public OAuth2AccessToken extractAccessToken(String value, Map map) {
        return tokenConverter.extractAccessToken(value, map);
    }

    @Override
    public OAuth2Authentication extractAuthentication(Map map) {
        return tokenConverter.extractAuthentication(map);
    }

    @Override
    protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        String content;
        try {
            content = objectMapper.formatMap(tokenConverter.convertAccessToken(accessToken, authentication));
        }
        catch (Exception e) {
            throw new IllegalStateException("Cannot convert access token to JSON", e);
        }
        String token = JwtHelper.encode(content, signer).getEncoded();
        return token;
    }
}

4、加载到 AuthorzationServerConfig.java

 /**
     * token增强,使用非对称加密算法来对Token进行签名
     *
     * @return JwtAccessTokenConverter
     */
    @Bean
    protected JwtAccessTokenConverter jwtAccessTokenConverter() {
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(keyProperties.getKeyStore().getLocation(), keyProperties.getKeyStore().getPassword().toCharArray());
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair(keyProperties.getKeyStore().getAlias()));
        converter.setAccessTokenConverter(new MyJwtAccessTokenConverter());
        return converter;
    }

到此oauth/token 获取到的access_token中就已经包含了userId的信息,(http://jwt.calebb.net/  解密网址)

oauth2 jwt token 返回accessToken对象中携带用户字段信息_第1张图片

 

接下来是资源服务器client的配置(为什么要配置资源服务器呢,第一次拿到token的时候可以看到上述json信息,如果想在上下文中获取当前用户信息就完蛋了,获取不到,如下:

public String getLoginUserName() {
        OAuth2Authentication authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null) {
//            用于不扩展token携带的用户信息时
//            return authentication.getUserAuthentication().getName();
//            自定义了token携带的用户信息之后,要另外获取
            return ((Map)authentication.getUserAuthentication().getPrincipal()).get("userName").toString();
        }
        return null;
    }

),在此之前要配置token转换器,也就是再重写一遍 DefaultAccessTokenConverter.java

package com.cloud.serviceclient.config;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 自定义CustomerAccessTokenConverter 这个类的作用主要用于AccessToken的转换,
 * 默认使用DefaultAccessTokenConverter 这个装换器
 * DefaultAccessTokenConverter有个UserAuthenticationConverter,这个转换器作用是把用户的信息放入token中,
 * 默认只是放入username
 * 

* 自定义了下这个方法,加入了额外的信息 *

*/ public class CustomerAccessTokenConverter extends DefaultAccessTokenConverter { public CustomerAccessTokenConverter() { super.setUserTokenConverter(new CustomerUserAuthenticationConverter()); } private class CustomerUserAuthenticationConverter extends DefaultUserAuthenticationConverter { public Authentication extractAuthentication(Map map) { Collection authorities = this.getAuthorities(map); return new UsernamePasswordAuthenticationToken(map, "N/A", authorities); } private Collection getAuthorities(Map map) { if (!map.containsKey("authorities")) { return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils.arrayToCommaDelimitedString(new String[]{"USER"})); } else { Object authorities = map.get("authorities"); if (authorities instanceof String) { return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities); } else if (authorities instanceof Collection) { return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils.collectionToCommaDelimitedString((Collection) authorities)); } else { throw new IllegalArgumentException("Authorities must be either a String or a Collection"); } } } } }

然后在resourceServerConfig中配置好:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;


@Configuration
@EnableResourceServer
@Order(1)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    TokenStore tokenStore;


    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .logout().logoutSuccessUrl("/login.html").permitAll().and()
                .authorizeRequests()
                .antMatchers("/login","/loginCode","/loginToken","/index","/logout","/login.html","/favicon.ico","/**/*.html","/**/*.js","/**/*.css","/img/**","/getToken","/token","/refreshToken","/actuator/**").permitAll()
                .antMatchers("/**").authenticated();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore);
        //添加异常处理
        resources.authenticationEntryPoint(new LLGAuthenticationEntryPoint());
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        Resource resource = new ClassPathResource("public.cert");
        String publicKey = null;
        try {
            publicKey = inputStream2String(resource.getInputStream());
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        converter.setVerifierKey(publicKey);
        converter.setAccessTokenConverter(new CustomerAccessTokenConverter());
        return converter;
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }

    String inputStream2String(InputStream is) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(is));
        StringBuffer buffer = new StringBuffer();
        String line = "";
        while ((line = in.readLine()) != null) {
            buffer.append(line);
        }
        return buffer.toString();
    }
}

方法二、结果是access_token中含有userId,并且返回的accessToken对象中显示userId,如图:

 

server 端:

1、重写 JwtAccessTokenConverter.java的部分方法

import com.langyatech.fcp.oauth.entity.OauthUser;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.jwt.crypto.sign.Signer;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.util.JsonParser;
import org.springframework.security.oauth2.common.util.JsonParserFactory;
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.HashMap;
import java.util.Map;

/**
 * 显式增加oauth/token返回的信息,token之外能看到新增的字段,token内也能看到
 * @author han
 */
public class MyJwtAccessTokenConverter2 extends JwtAccessTokenConverter {

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        if (accessToken instanceof DefaultOAuth2AccessToken) {
            Object principal = authentication.getPrincipal();

            if (principal instanceof OauthUser) {
                OauthUser user = (OauthUser) principal;
                HashMap map = new HashMap<>();

                map.put("user_id", user.getUserId());
                map.put("phone", user.getPhone());
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(map);
            }
        }
        return super.enhance(accessToken, authentication);
    }

}

2、然后把这个类配置到 AuthorizationServerConfig.java

@Bean
    protected JwtAccessTokenConverter jwtAccessTokenConverter() {
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(keyProperties.getKeyStore().getLocation(), keyProperties.getKeyStore().getPassword().toCharArray());
        JwtAccessTokenConverter converter = new MyJwtAccessTokenConverter2();
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair(keyProperties.getKeyStore().getAlias()));
        return converter;
    }

贴一下得到的token对象:

oauth2 jwt token 返回accessToken对象中携带用户字段信息_第2张图片 user_id 和phone 都带出来了,也可以通过jwthelp解析token获取。

oauth2 jwt token 返回accessToken对象中携带用户字段信息_第3张图片

2、client端不需要方法一中配置token转换器和tokenstore等信息,公钥也不用用resource加载了,resourceServerProperties.java会自动配置。

总结来说还是第二种方式较好

最后pom文件贴一下



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.5.RELEASE
         
    
    com.cloud
    service-client
    0.0.1-SNAPSHOT
    service-client
    service-client project for Spring Boot

    
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.cloud
            spring-cloud-starter-config
            2.1.2.RELEASE
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
            2.1.1.RELEASE
        
        
            org.springframework.cloud
            spring-cloud-starter-zipkin
            2.1.1.RELEASE
        
        
            org.springframework.cloud
            spring-cloud-starter-security
        
        
            
            
            
        
        
            org.springframework.security
            spring-security-jwt
            1.0.9.RELEASE
        
        
            org.springframework.security.oauth
            spring-security-oauth2
            2.3.4.RELEASE
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.0.1
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
            mysql
            mysql-connector-java
            runtime
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
            2.1.1.RELEASE
        
        
            io.github.openfeign
            feign-httpclient
        
        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-hystrix
            2.1.1.RELEASE
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            org.projectlombok
            lombok
            1.18.6
            provided
        

        
            io.jsonwebtoken
            jjwt
            0.9.0
        
        
            org.apache.commons
            commons-collections4
            4.1
        
    

    
        
            
                
                
                
                
                
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
            
            
                org.apache.maven.plugins
                maven-resources-plugin
                
                    
                        cert
                        jks
                    
                
            
        
    


 

你可能感兴趣的:(oauth2 jwt token 返回accessToken对象中携带用户字段信息)