前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多维系统下单点登录之生产实践(2种方案3种实践)

多维系统下单点登录之生产实践(2种方案3种实践)

作者头像
小熊学Java
发布2024-09-24 18:47:32
840
发布2024-09-24 18:47:32
举报
文章被收录于专栏:全栈学习之路

1、基于 Cookie 跨域与分布式 Session 的技术实践

1、XXL-SSO 整体价格

2、实现原理剖析

  • 首次请求
  • 第二次请求
  • 跨域请求
  • 注销流程

3、案例演示

  • 首次登陆跳转至统一认证中心

访问:http://xxlssoclient1.com:8081/

  • 登陆成功,写入 Cookie,保存 sessionId 信息
  • 跨域访问 访问另外一个域名:http://xxlssoclient2.com:8081/ 自动登陆,并且写入 SessionId 至 Cookie 当中

4、代码实现剖析

采用 Debug 方式跟踪解析。关键断点:

  • 统一认证服务, 登陆入口:WebController 的 login 方法
  • 应用服务 Web 过滤器:XxlSsoWebFilter 的 doFilter 方法
  • 应用服务登陆注销:XxlSsoWebFilter 的 doFilter 方法,Line: 64

2、基于 Token 增强的微服务技术实践

1、整体流程

采用密码模式,基于 Token 增强的微服务应用实现方案

2、代码实现

1、认证服务

认证服务配置AuthorizationServerConfig

代码语言:javascript
复制
/**
 * 自定义Client查询,可以修改表名, 字段等
 * @param clients
 */
@
Override@ SneakyThrows
public void configure(ClientDetailsServiceConfigurer clients) {
        AuthClientDetailService clientDetailsService = new
        AuthClientDetailService(dataSource);
        clientDetailsService.setSelectClientDetailsSql(DEFAULT_SELECT_STATEMEN T);
        clientDetailsService.setFindClientDetailsSql(DEFAULT_FIND_STATEMENT);
        clients.withClientDetails(clientDetailsService);
    }
    /**
     * 防止申请token时出现401错误
     * @param oauthServer
     */
    @
Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer
            .tokenKeyAccess("permitAll()")
            .checkTokenAccess("permitAll()")
            .allowFormAuthenticationForClients();
    }
    /**
     * 认证服务配置
     * @param endpoints
     */
    @
Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
            .allowedTokenEndpointRequestMethods(HttpMethod.GET,
                HttpMethod.POST)
            .tokenStore(tokenStore())
            .tokenEnhancer(tokenEnhancer())
            .userDetailsService(authStockUserDetailService)
            .authenticationManager(authenticationManager)
            .reuseRefreshTokens(false);
    }
    /**
     * TokenStore实现方式, 采用Redis缓存
     * @return
     */
    @
Bean
public TokenStore tokenStore() {
        RedisTokenStore tokenStore = new
        RedisTokenStore(redisConnectionFactory);
        tokenStore.setPrefix(GlobalConstants.OAUTH_PREFIX_KEY);
        tokenStore.setAuthenticationKeyGenerator(new DefaultAuthenticationKeyGenerator() {@
            Override
            public String extractKey(OAuth2Authentication authentication) {
                return super.extractKey(authentication);
            }
        });
        return tokenStore;
    }
    /**
     * token增强处理, 支持扩展信息
     * @return TokenEnhancer
     */
    @
Bean
public TokenEnhancer tokenEnhancer() {
    return (accessToken, authentication) - > {
        try {
            if (GlobalConstants.OAUTH_CLIENT_CREDENTIALS
                .equals(authentication.getOAuth2Request().getGrantType())) {
                return accessToken;
            }
            // 通过MAP 存储附加的信息
            final Map < String, Object > additionalInfo = new
            HashMap < > (16);
            OAuthTradeUser authTradeUser = (OAuthTradeUser)
            authentication.getUserAuthentication().getPrincipal();
            if (null != authTradeUser) {
                TradeUser tradeUser = authTradeUser.getTradeUser();
                // 需要扩充增加的用户附带信息
                additionalInfo.put(GlobalConstants.OAUTH_DETAILS_USER_ID,
                    tradeUser.getId());
                additionalInfo.put(GlobalConstants.OAUTH_DETAILS_USERNAME,
                    tradeUser.getName());
                additionalInfo.put(GlobalConstants.OAUTH_DETAILS_LOGIN_INFO,
                    tradeUser.getEmail() + "|" + tradeUser.getAddress());
                additionalInfo.put("active", true);
            }
            // 将附加的信息记录保存, 形成增强的TOKEN
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return accessToken;
    };
}

用户信息服务接口AuthStockUserDetailServiceImpl

代码语言:javascript
复制
import com.itcast.bulls.stock.trade.oauth.repository.TradeUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service("authStockUserDetailService")
public class AuthStockUserDetailServiceImpl implements UserDetailsService {

    /**
     * 用户的数据层接口
     */
    @Autowired
    private TradeUserRepository tradeUserRepository;

    /**
     * 缓存管理接口
     */
    @Autowired
    private CacheManager cacheManager;

    /**
     * 根据用户账号获取用户对象接口
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String userNo) throws UsernameNotFoundException {
        // 1. 从缓存中查找用户对象
        Cache cache = cacheManager.getCache(GlobalConstants.OAUTH_KEY_STOCK_USER_DETAILS);
        if(null != cache && null != cache.get(userNo)) {
            return (UserDetails)cache.get(userNo).get();
        }

        // 2. 如果缓存未找到, 查询数据库
        TradeUser tradeUser = tradeUserRepository.findByUserNo(userNo);
        if(null == tradeUser) {
            throw new UsernameNotFoundException(userNo + " not valid! ");
        }

        // 3. 对用户信息做封装处理
        UserDetails userDetails = new OAuthTradeUser(tradeUser);

        // 4. 将封装的用户信息放入到缓存当中
        cache.put(userNo, userDetails);
        return userDetails;
    }
}

这是 Spring Security 提供的用户信息接口, 采用 OAUTH 的密码模式, 需要实现该接口的 loadUserByUsername 方法,为提升性能, 这里我们加入了 Spring Cache 缓存处理。

自定义用户信息: OAuthTradeUser

代码语言:javascript
复制
public class OAuthTradeUser extends User {

    private static final long serialVersionUUID = -1L;

    /**
     * 业务用户信息
     */
    private TradeUser tradeUser;

    public OAuthTradeUser(TradeUser tradeUser) {
        // OAUTH2认证用户信息构造处理
        super(tradeUser.getUserNo(), tradeUser.getUserPwd(), (tradeUser.getStatus() == 0 ? true : false),
                true, true, (tradeUser.getStatus() == 0 ? true : false), Collections.emptyList());
        this.tradeUser = tradeUser;
    }

    public TradeUser getTradeUser() {
        return tradeUser;
    }
}

客户端信息服务接口AuthClientDetailService

代码语言:javascript
复制
public class AuthClientDetailService extends JdbcClientDetailsService {

    public AuthClientDetailService(DataSource dataSource) {
        super(dataSource);
    }

    /**
     * 重写原生方法支持redis缓存
     *
     * @param clientId
     * @return
     * @throws InvalidClientException
     */
    @Override
    @Cacheable(value = GlobalConstants.OAUTH_KEY_CLIENT_DETAILS, key = "#clientId", unless = "#result == null")
    public ClientDetails loadClientByClientId(String clientId) {
        return super.loadClientByClientId(clientId);
    }
}

这是 OAUTH 内置的客户端信息, 重新它是为了实现缓存, 减少数据库查询。

2、用户服务

认证配置ResourceSecurityConfigurer

代码语言:javascript
复制
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
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.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
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.ResourceServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
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 org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;

@Primary
@Order(90)
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceSecurityConfigurer implements ResourceServerConfigurer {

    @Autowired
    private RemoteTokenServices remoteTokenServices;

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 远程调用, 采用RestTemplate方式
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        remoteTokenServices.setRestTemplate(restTemplate);
        resources.tokenServices(remoteTokenServices);
    }

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

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


    /**
     * 资源服务的安全配置
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/user/**").authenticated().and()
                .formLogin().loginPage("/login")
                .failureUrl("/login?error")
                .defaultSuccessUrl("/home");

    }

    /**
     * RestTemplate配置
     * @return
     */
    @Bean
    @Primary
    @LoadBalanced
    public RestTemplate lbRestTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                if (response.getRawStatusCode() != HttpStatus.BAD_REQUEST.value()) {
                    super.handleError(response);
                }
            }
        });
        return restTemplate;
    }

}

用户服务为资源服务, 认证采用 RestTemplate 调用方式。资源服务一定要开启@EnableResourceServer注解, @EnableGlobalMethodSecurity为方法级别安全控制。

提供获取用户增强信息接口StockUserController

代码语言:javascript
复制
import com.itcast.bulls.stock.common.exception.ComponentException;
import com.itcast.bulls.stock.entity.user.TradeUser;
import com.itcast.stock.common.web.vo.ApiRespResult;
import com.itcast.trade.bulls.stock.user.service.IStockUserService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController()
@RequestMapping("/user")
@Log4j2
public class StockUserController extends BaseController{

    @Autowired
    private IStockUserService stockUserService;

    /**
     * 用户登陆接口
     * @param userNo
     * @param userPwd
     * @return
     */
    @RequestMapping("/userLogin")
    public ApiRespResult userLogin(@RequestParam("userNo")String userNo, @RequestParam("userPwd") String userPwd) {

        ApiRespResult  result = null;
        try {
            // 用户登陆逻辑处理
            TradeUser tradeUser = stockUserService.userLogin(userNo, userPwd);
            result = ApiRespResult.success(tradeUser);
        }catch(ComponentException e) {
            log.error(e.getMessage(), e);
            result = ApiRespResult.error(e.geterrorCodeEnum());
        }catch(Exception e) {
            log.error(e.getMessage(), e);
            result = ApiRespResult.sysError(e.getMessage());
        }

        return result;

    }

    /**
     * 获取用户JWT扩展信息
     * @return
     */
    @RequestMapping("/getJwtInfo")
    public ApiRespResult getUserEnhancer() {

        ApiRespResult  result = null;
        try {
            // 获取用户JWT扩展信息
            Map<String, Object> userAdditionalInfos = getUserAdditionalInfos();
            result = ApiRespResult.success(userAdditionalInfos);
        }catch(ComponentException e) {
            log.error(e.getMessage(), e);
            result = ApiRespResult.error(e.geterrorCodeEnum());
        }catch(Exception e) {
            log.error(e.getMessage(), e);
            result = ApiRespResult.sysError(e.getMessage());
        }
        return result;
    }
}
异常组件
代码语言:javascript
复制
import com.itcast.bulls.stock.common.exception.constants.IErrorCodeEnum;

/**
 * 自定义组件异常
 */
public class ComponentException extends AbstractException {

 /**
  *
  */
 private static final long serialVersionUID = 2333790764399190094L;

    /**
     * 错误码枚举信息
     */
 private IErrorCodeEnum errorCodeEnum;

    /**
     * 扩展的错误信息
     */
 private String extendErrorMessage;


    public ComponentException(IErrorCodeEnum errorCodeEnum) {
        super(errorCodeEnum.getCode() + ":" + errorCodeEnum.getMessage());
        this.errorCodeEnum = errorCodeEnum;
    }


 public ComponentException(IErrorCodeEnum errorCodeEnum, String extendErrorMessage) {
  super(errorCodeEnum.getCode() + ":" + errorCodeEnum.getMessage() + "["
    + extendErrorMessage + "]");
  this.errorCodeEnum = errorCodeEnum;
  this.extendErrorMessage = extendErrorMessage;
 }


 public IErrorCodeEnum geterrorCodeEnum() {
  return errorCodeEnum;
 }

 public void seterrorCodeEnum(IErrorCodeEnum errorCodeEnum) {
  this.errorCodeEnum = errorCodeEnum;
 }

 public String getExtendErrorMessage() {
  return extendErrorMessage;
 }

 public void setExtendErrorMessage(String extendErrorMessage) {
  this.extendErrorMessage = extendErrorMessage;
 }

}
3、网关服务

全局过滤器StockRequestGlobalFilter

代码语言:javascript
复制
import io.netty.util.internal.StringUtil;
import lombok.extern.log4j.Log4j2;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
@Log4j2
public class StockRequestGlobalFilter implements GlobalFilter, Ordered {

    /**
     * 通过filter来自定义配置转发信息
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String authentication = exchange.getRequest().getHeaders().getFirst("Authorization");
        if(!StringUtil.isNullOrEmpty(authentication)){
            log.info("enter stockRequestGlobalFilter filter method: " + authentication);
            exchange.getRequest().mutate().header("Authorization",authentication);
        }
        return chain.filter(exchange.mutate().build());
    }

    @Override
    public int getOrder() {
        return -1000;
    }
}

这是自定义全局过滤器的实现, 防止 header 中的 Authorization 没有转发的问题

3、测试

申请 Toke

代码语言:javascript
复制
POST http://127.0.0.1:10680/oauth/token?
grant_type=password&username=admin&password=admin&scope=server
Accept: */*
Cache-Control: no-cache
Authorization: Basic YXBwOmFwcA==

返回 Token 信息:

代码语言:javascript
复制
{
"access_token": "cc5c4c1d-b519-458f-b338-ad4bd1ec06b0",
"token_type": "bearer",
"refresh_token": "86fec4ff-6c24-4171-a257-bf2d4e6bc30c",
"expires_in": 29749,
"scope": "server",
"login_info": "hekun1@itcast.cn|null",
"user_id": 1,
"user_name": "admin",
"active": true
}
1234567891011

获取增强用户信息

代码语言:javascript
复制
GET 127.0.0.1:10680/user/getUserEnhancer
Accept: */*
Cache-Control: no-cache
Authorization: Bearer cc5c4c1d-b519-458f-b338-ad4bd1ec06b0
1234

返回增强的用户信息:

代码语言:javascript
复制
{
"code": "SYS_200",
"msg": "成功",
"extendData": null,
"data": {
"login_info": "hekun1@itcast.cn|null",
"user_id": 1,
"user_name": "admin",
"active": true
},
"success": true
}
123456789101112

3、基于 JWT 扩展信息的微服务技术实践

1、整体流程

整体实现流程:采用密码模式,基于 JWT 扩展信息的微服务应用实践方案:

2、代码实现

1、认证服务

认证服务系统配置AuthorizationServerConfig

代码语言:javascript
复制
 /**
     * 认证服务配置
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        // JWT信息增强配置,采用链式配置, 包含JWT签名配置与JWT扩展信息配置。
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer());
        delegates.add(accessTokenConverter());
        enhancerChain.setTokenEnhancers(delegates);

        endpoints
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                .tokenStore(tokenStore())
                .userDetailsService(authStockUserDetailService)
                .authenticationManager(authenticationManager)
                .reuseRefreshTokens(false)
                .tokenEnhancer(enhancerChain);
    }

    @Bean
    public JwtTokenEnhancer jwtTokenEnhancer() {
        return new JwtTokenEnhancer();
    }
    /**
     * TokenStore实现方式, 采用Redis缓存
     * @return
     */
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }


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

12345678910111213141516171819202122232425262728293031323334353637383940414243

认证服务采用 JWT 方式配置,JWT 配置,采用链式配置, 包含 JWT 签名配置与 JWT 扩展信息,JWT 签名设为 test123。这里采用自定义的增强 JWT 作实现。

JWT 增强实现类:JwtTokenEnhancer
代码语言:javascript
复制
public class JwtTokenEnhancer implements TokenEnhancer {

    /**
     * JWT扩展存储用户信息
     * @param accessToken
     * @param authentication
     * @return
     */
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        Map<String, Object> additionalInfo = new HashMap<>();
        OAuthTradeUser authTradeUser = (OAuthTradeUser) authentication.getUserAuthentication().getPrincipal();
        if(null != authTradeUser) {
            TradeUser tradeUser = authTradeUser.getTradeUser();
            // 存储用户扩展信息
            additionalInfo.put(GlobalConstants.OAUTH_DETAILS_USER_ID, tradeUser.getId());
            additionalInfo.put(GlobalConstants.OAUTH_DETAILS_USERNAME, tradeUser.getName());
            additionalInfo.put(GlobalConstants.OAUTH_DETAILS_LOGIN_INFO, tradeUser.getEmail() + "|" + tradeUser.getAddress());
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
        }
        return accessToken;
    }
}

123456789101112131415161718192021222324

在 JWT 存储扩展用户信息,可以根据需要扩展不同的信息,但长度要有限制。

2、用户服务

认证配置ResourceSecurityConfigurer

代码语言:javascript
复制
@Primary
@Order(90)
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceSecurityConfigurer implements ResourceServerConfigurer {

    @Autowired
    private RemoteTokenServices remoteTokenServices;

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 远程调用, 采用RestTemplate方式
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        remoteTokenServices.setRestTemplate(restTemplate);
        resources.tokenServices(remoteTokenServices);
    }

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

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


    /**
     * 资源服务的安全配置
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/user/**").authenticated().and()
                .formLogin().loginPage("/login")
                .failureUrl("/login?error")
                .defaultSuccessUrl("/home");

    }

    /**
     * RestTemplate配置
     * @return
     */
    @Bean
    @Primary
    @LoadBalanced
    public RestTemplate lbRestTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                if (response.getRawStatusCode() != HttpStatus.BAD_REQUEST.value()) {
                    super.handleError(response);
                }
            }
        });
        return restTemplate;
    }

}

修改认证配置,采用 JWT 方式,设置签名为 test123,这里要和认证服务里面的签名保持一致,否则不能正常解析 JWT 信息。

增加获取 JWT 扩展信息的接口StockUserController
代码语言:javascript
复制
    /**
     * 获取用户JWT扩展信息
     * @return
     */
    @RequestMapping("/getJwtInfo")
    public ApiRespResult getUserEnhancer() {

        ApiRespResult  result = null;
        try {
            // 获取用户JWT扩展信息
            Map<String, Object> userAdditionalInfos = getUserAdditionalInfos();
            result = ApiRespResult.success(userAdditionalInfos);
        }catch(ComponentException e) {
            log.error(e.getMessage(), e);
            result = ApiRespResult.error(e.geterrorCodeEnum());
        }catch(Exception e) {
            log.error(e.getMessage(), e);
            result = ApiRespResult.sysError(e.getMessage());
        }

        return result;

    }

自定义解析 JWT 数据增加依赖:

代码语言:javascript
复制
 <!-- JWT TOKEN 组件 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

  protected String getJwtToken() {
        // 1. 获取Request对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 2. 获取token信息
        String token = request.getHeader("Authorization");
        if(null != token) {
            token = token.replace(OAuth2AccessToken.BEARER_TYPE, "").trim();
        }
        return Jwts.parser()
                .setSigningKey("test123".getBytes(StandardCharsets.UTF_8))
                .parseClaimsJws(token)
                .getBody().toString();
    }

3、测试验证

申请 Token

代码语言:javascript
复制
POST http://127.0.0.1:10680/oauth/token?
grant_type=password&username=admin&password=admin&scope=server
Accept: */*
Cache-Control: no-cache
Authorization: Basic YXBwOmFwcA==

返回 Token 信息:

代码语言:javascript
复制
{
"access_token":
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbl9pbmZvIjoiaGVrdW4xQGl0Y
2FzdC5jbnxudWxsIiwidXNlcl9pZCI6MSwidXNlcl9uYW1lIjoiYWRtaW4iLCJzY29wZSI6
WyJzZXJ2ZXIiXSwiZXhwIjoxNTk0ODQ5NTg1LCJqdGkiOiI2OWI4MWQzMi05MTk2LTQ5YmI
tOTU3ZC05YmRlZDM2OTY3ZTAiLCJjbGllbnRfaWQiOiJhcHAifQ.bFBKhPf0IYnJ9dpZG4a
PIlmpLECYwK-jTYTPHd2fc_M",
"token_type": "bearer",
"refresh_token":
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbl9pbmZvIjoiaGVrdW4xQGl0Y
2FzdC5jbnxudWxsIiwidXNlcl9pZCI6MSwidXNlcl9uYW1lIjoiYWRtaW4iLCJzY29wZSI6
WyJzZXJ2ZXIiXSwiYXRpIjoiNjliODFkMzItOTE5Ni00OWJiLTk1N2QtOWJkZWQzNjk2N2U
wIiwiZXhwIjoxNTk3Mzk4Mzg1LCJqdGkiOiIyMjhkMmIyZS02YmRkLTQ1NzktYTljNy03ZG
I0NmZmMjA3ZjkiLCJjbGllbnRfaWQiOiJhcHAifQ.yHD0U1WtOH_SAGev3mPwD1L1_XucWv
tRpTT-upHNqTM",
"expires_in": 43199,
"scope": "server",
"login_info": "hekun1@itcast.cn|null",
"user_id": 1,
"user_name": "admin",
"jti": "69b81d32-9196-49bb-957d-9bded36967e0"
}

获取 JWT 扩展用户信息

代码语言:javascript
复制
GET 127.0.0.1:10680/user/getJwtInfo
Accept: */*
Cache-Control: no-cache
Authorization: Bearer
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbl9pbmZvIjoiaGVrdW4xQGl0Y2
FzdC5jbnxudWxsIiwidXNlcl9pZCI6MSwidXNlcl9uYW1lIjoiYWRtaW4iLCJzY29wZSI6W
yJzZXJ2ZXIiXSwiZXhwIjoxNTk0ODQ5NTg1LCJqdGkiOiI2OWI4MWQzMi05MTk2LTQ5YmIt
OTU3ZC05YmRlZDM2OTY3ZTAiLCJjbGllbnRfaWQiOiJhcHAifQ.bFBKhPf0IYnJ9dpZG4aP
IlmpLECYwK-jTYTPHd2fc_M

返回 JWT 扩展用户信息:

代码语言:javascript
复制
{
"code": "SYS_200",
"msg": "成功",
"extendData": null,
"data": {
"login_info": "hekun1@itcast.cn|null",
"user_id": 1,
"user_name": "admin",
"jti": "69b81d32-9196-49bb-957d-9bded36967e0"
},
"success": true
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-09-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小熊学Java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、基于 Cookie 跨域与分布式 Session 的技术实践
    • 1、XXL-SSO 整体价格
      • 2、实现原理剖析
        • 3、案例演示
          • 4、代码实现剖析
          • 2、基于 Token 增强的微服务技术实践
            • 1、整体流程
              • 2、代码实现
                • 1、认证服务
                • 2、用户服务
                • 3、网关服务
              • 3、测试
              • 3、基于 JWT 扩展信息的微服务技术实践
                • 1、整体流程
                  • 2、代码实现
                    • 1、认证服务
                    • 2、用户服务
                  • 3、测试验证
                  相关产品与服务
                  云数据库 Redis
                  腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档