IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Oauth2协议及自定义Demo的实现 -> 正文阅读

[网络协议]Oauth2协议及自定义Demo的实现

Oauth介绍

OAuth全称是:Open Authentication

  • 开放授权(OAuth)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
  • OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站在特定的时段内访问特定的资源。OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。

Oauth2.0授权模型

Oauth2.0支持四种授权模式:

1.密码模式(username&password)

  • 这种模式类似于QQ登录等
  • 支持refresh token

2.授权码模式(authorization code)

  • 设计了auth code,通过auth code再获取token
  • token放在服务端,不经历客户端,防止token泄露等
  • 支持refresh token

3.简化模式(implicit)

  • 这种模式比较授权码模式少了code环节,回调的url直接携带token
  • 不支持refresh token

4.客户端模式(client credentials)

  • 这种模式直接根据client的id和密钥即可获取token,无需用户参与
  • 这种模式比较合适消费api的后端服务
  • 不支持refresh token

简单介绍一下refresh token,access token即token申请到后是会存在过期时间,申请token的同时,授权服务器通常会伴随着refresh token进行授予(前提是支持refresh token的模式),当token过期时,就可以直接使用refresh token重新向授权服务器重新申请access token,就不需要重新验证。

整合Demo

授权访问的流程

在这里插入图片描述
要实现这个Demo,我们先从源码出发,从获取token的源头出发

package org.springframework.security.oauth2.provider.endpoint;

@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint {
    private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();
    private Set<HttpMethod> allowedRequestMethods;

    public TokenEndpoint() {
        this.allowedRequestMethods = new HashSet(Arrays.asList(HttpMethod.POST));
    }

	//得到Token的请求url
    @RequestMapping(
        value = {"/oauth/token"},
        method = {RequestMethod.GET}
    )
    public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        if (!this.allowedRequestMethods.contains(HttpMethod.GET)) {
        	//判断get请求是否在此允许 allowedRequestMethods集合中封装了允许的方法的宏定义
            throw new HttpRequestMethodNotSupportedException("GET");
        } else {
        	//允许则将方法跳转到postAccessToken,即接下来跟着的
            return this.postAccessToken(principal, parameters);
        }
    }

    @RequestMapping(
        value = {"/oauth/token"},
        method = {RequestMethod.POST}
    )
    public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
    	// 首先判断是否获取到了授权码
        if (!(principal instanceof Authentication)) {
            throw new InsufficientAuthenticationException("There is no client authentication. Try adding an appropriate authentication filter.");
        } else {
        	// 从授权码信息中将客户端ID提取出来
            String clientId = this.getClientId(principal);
            // 从客户端信息存储库中通过clientID进行加载客户端信息,接下来我们的Demo将这个客户端信息存储到Redis中
            ClientDetails authenticatedClient = this.getClientDetailsService().loadClientByClientId(clientId);
            // 构造一个TokenRequest 将发送给授权服务器
            TokenRequest tokenRequest = this.getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
            if (clientId != null && !clientId.equals("") && !clientId.equals(tokenRequest.getClientId())) {
                throw new InvalidClientException("Given client ID does not match authenticated client");
            } else {
                if (authenticatedClient != null) {
                    // 设置token的验证范围
                    this.oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
                }

                if (!StringUtils.hasText(tokenRequest.getGrantType())) {
                    throw new InvalidRequestException("Missing grant type");
                } else if (tokenRequest.getGrantType().equals("implicit")) {
                    throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
                } else {
                    if (this.isAuthCodeRequest(parameters) && !tokenRequest.getScope().isEmpty()) {
                        this.logger.debug("Clearing scope of incoming token request");
                        tokenRequest.setScope(Collections.emptySet());
                    }

                    if (this.isRefreshTokenRequest(parameters)) {
                        tokenRequest.setScope(OAuth2Utils.parseParameterList((String)parameters.get("scope")));
                    }
					// 真正生成token的函数入口 这里是接口的实现,需要后来配置
                    OAuth2AccessToken token = this.getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
                    if (token == null) {
                        throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
                    } else {
                        return this.getResponse(token);
                    }
                }
            }
        }
    }
	...省略了部分异常处理器的声明...
    protected String getClientId(Principal principal) {
        Authentication client = (Authentication)principal;
        if (!client.isAuthenticated()) {
            throw new InsufficientAuthenticationException("The client is not authenticated.");
        } else {
            String clientId = client.getName();
            if (client instanceof OAuth2Authentication) {
                clientId = ((OAuth2Authentication)client).getOAuth2Request().getClientId();
            }

            return clientId;
        }
    }


    private ResponseEntity<OAuth2AccessToken> getResponse(OAuth2AccessToken accessToken) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        headers.set("Content-Type", "application/json;charset=UTF-8");
        return new ResponseEntity(accessToken, headers, HttpStatus.OK);
    }
}

授权码的获取逻辑,该类是AuthorizationEndpoint

@RequestMapping({"/oauth/authorize"})
    public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters, SessionStatus sessionStatus, Principal principal) {
    	// 同样是构建授权码请求
        AuthorizationRequest authorizationRequest = this.getOAuth2RequestFactory().createAuthorizationRequest(parameters);
        Set<String> responseTypes = authorizationRequest.getResponseTypes();
        if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
            throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
        } else if (authorizationRequest.getClientId() == null) {
            throw new InvalidClientException("A client id must be provided");
        } else {
            try {
                if (principal instanceof Authentication && ((Authentication)principal).isAuthenticated()) {
                    // 加载客户端信息
                    ClientDetails client = this.getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
                    // 设置重定向参数
                    String redirectUriParameter = (String)authorizationRequest.getRequestParameters().get("redirect_uri");
                    String resolvedRedirect = this.redirectResolver.resolveRedirect(redirectUriParameter, client);
                    if (!StringUtils.hasText(resolvedRedirect)) {
                        throw new RedirectMismatchException("A redirectUri must be either supplied or preconfigured in the ClientDetails");
                    } else {
                        authorizationRequest.setRedirectUri(resolvedRedirect);
                        this.oauth2RequestValidator.validateScope(authorizationRequest, client);
                        authorizationRequest = this.userApprovalHandler.checkForPreApproval(authorizationRequest, (Authentication)principal);
                        boolean approved = this.userApprovalHandler.isApproved(authorizationRequest, (Authentication)principal);
                        // 是否批准
                        authorizationRequest.setApproved(approved);
                        if (authorizationRequest.isApproved()) {
                            if (responseTypes.contains("token")) {
                                return this.getImplicitGrantResponse(authorizationRequest);
                            }

                            if (responseTypes.contains("code")) {
                                return new ModelAndView(this.getAuthorizationCodeResponse(authorizationRequest, (Authentication)principal));
                            }
                        }

                        model.put("authorizationRequest", authorizationRequest);
                        model.put("org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST", this.unmodifiableMap(authorizationRequest));
                        return this.getUserApprovalPageResponse(model, authorizationRequest, (Authentication)principal);
                    }
                } else {
                    throw new InsufficientAuthenticationException("User must be authenticated with Spring Security before authorization can be completed.");
                }
            } catch (RuntimeException var11) {
                sessionStatus.setComplete();
                throw var11;
            }
        }
    }

小总结:
获取Token主要做了这几件事:

  • 是否获得授权码
  • 通过ClientDetailsService加载客户端信息
  • 构造TokenRequest请求
  • 调用TokenGranter的grant()得到Token

Demo的实现

AuthorizationServerConfig (授权服务器的Endpoint配置)

@Component
    @Configuration
    @EnableAuthorizationServer //认证服务器的配置
    @AutoConfigureAfter(AuthorizationServerEndpointsConfigurer.class)
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
        /**
         * 注入authenticationManager 来支持 password grant type
         */
        @Autowired
        private AuthenticationManager authenticationManager;

        // 用户服务
        @Autowired
        private UserDetailsService userDetailsService;

        // 验证码服务
        @Autowired
        private ValidateCodeService validateCodeService ;
        
        @Autowired(required = false)
        private TokenStore tokenStore;

        // converter 转换器
        @Autowired(required = false)
        private JwtAccessTokenConverter jwtAccessTokenConverter;

        // translator 翻译
        @Autowired
        private WebResponseExceptionTranslator webResponseExceptionTranslator;

        @Autowired
        private RedisClientDetailsService redisClientDetailsService;

        // 这里注入的是redis 授权码模式
        @Autowired(required = false)
        private RandomValueAuthorizationCodeServices authorizationCodeServices;


        /**
         * 配置身份认证器,配置认证方式,TokenStore,TokenGranter,OAuth2RequestFactory
         */
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

            //通用处理
            // 配置token存储 配置验证管理 配置用户服务
            endpoints.tokenStore(tokenStore).authenticationManager(authenticationManager)
                    // 支持
                    .userDetailsService(userDetailsService);

            if (tokenStore instanceof JwtTokenStore) {
                endpoints.accessTokenConverter(jwtAccessTokenConverter);
            }

            //处理授权码 使用的是redis
            endpoints.authorizationCodeServices(authorizationCodeServices);
            // 处理 ExceptionTranslationFilter 抛出的异常
            endpoints.exceptionTranslator(webResponseExceptionTranslator);
            
            //处理oauth 模式
            ClientDetailsService clientDetails = endpoints.getClientDetailsService();
            //生成token的工具类
            AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices();
            //生成授权码的工具类
            AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices();
            OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory();
     
            //tokenGranters添加oauth模式 ,可以让/oauth/token支持自定义模式,继承AbstractTokenGranter 扩展 
            List<TokenGranter> tokenGranters = new ArrayList<>();
            tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
            //密码模式	  GRANT_TYPE = "password"; 	密码模式 需要authenticationManager 和验证码服务
            tokenGranters.add(new PasswordEnhanceTokenGranter(authenticationManager, tokenServices,clientDetails, requestFactory,validateCodeService));
            //授权码模式   GRANT_TYPE = "authorization_code";  需要授权码
            tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails,requestFactory));
            //刷新模式	  GRANT_TYPE = "refresh_token";
            tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
            //简易模式	  GRANT_TYPE = "implicit";
            tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory));
            //短信模式	  GRANT_TYPE = "sms"; 参考ResourceOwnerPasswordTokenGranter重写
            tokenGranters.add(new SMSCodeTokenGranter( userDetailsService,  validateCodeService  ,  tokenServices, clientDetails, requestFactory));
            //组合模式
            endpoints.tokenGranter(new CompositeTokenGranter(tokenGranters));
        }
  • 上面已经AccessToken是通过tokenGranters中具体的Granter生成的,我们这里讲其中的一种授权码模式如何生成的
    AuthorizationCodeTokenGranter
	new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails,requestFactory)
	
	...
public class AuthorizationCodeTokenGranter extends AbstractTokenGranter {
	... 省略部分代码 ...
    private static final String GRANT_TYPE = "authorization_code";
    private final AuthorizationCodeServices authorizationCodeServices;
    
    实际上是初始化了tokenServices(token生成服务),authorizationCodeServices(授权码生成服务),clientDetailsService(客户端信息服务),requestFactory(请求构造器)
    public AuthorizationCodeTokenGranter(AuthorizationServerTokenServices tokenServices, AuthorizationCodeServices authorizationCodeServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
        this(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory, "authorization_code");
    }

    protected AuthorizationCodeTokenGranter(AuthorizationServerTokenServices tokenServices, AuthorizationCodeServices authorizationCodeServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
        super(tokenServices, clientDetailsService, requestFactory, grantType);
        this.authorizationCodeServices = authorizationCodeServices;
    }
}
  • 再来看AbstractTokenGranter类,这里实现了具体的逻辑
    public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
        if (!this.grantType.equals(grantType)) {
            return null;
        } else {
            String clientId = tokenRequest.getClientId();
            ClientDetails client = this.clientDetailsService.loadClientByClientId(clientId);
            this.validateGrantType(grantType, client);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Getting access token for: " + clientId);
            }
			//重点是这个函数
            return this.getAccessToken(client, tokenRequest);
        }
    }
  • getAccessToken
    protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
    	// 通过tokenServices来创建accessToken
        return this.tokenServices.createAccessToken(this.getOAuth2Authentication(client, tokenRequest));
    }
  • 由于AuthorizationServerTokenServices是个接口,我们查看DefaultTokenServicescreateAccessToken
    @Transactional 开启事务
    public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
    	// 从tokenStore看是否能拿到accessToken 我们的Demo中使用Redis当作token 存储源
        OAuth2AccessToken existingAccessToken = this.tokenStore.getAccessToken(authentication);
        OAuth2RefreshToken refreshToken = null;
        if (existingAccessToken != null) {
        	// 判断是否过期 未过期则直接返回
            if (!existingAccessToken.isExpired()) {
                this.tokenStore.storeAccessToken(existingAccessToken, authentication);
                return existingAccessToken;
            }
			// 过期则先判断是否存在 RefreshToken 存在则删除
            if (existingAccessToken.getRefreshToken() != null) {
                refreshToken = existingAccessToken.getRefreshToken();
                this.tokenStore.removeRefreshToken(refreshToken);
            }
			// 然后再删除自身
            this.tokenStore.removeAccessToken(existingAccessToken);
        }
		// 如果refreshToken 为空,则先创建RefreshToken
        if (refreshToken == null) {
            refreshToken = this.createRefreshToken(authentication);
        } else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
            ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken)refreshToken;
            if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
                refreshToken = this.createRefreshToken(authentication);
            }
        }
		// 然后创建accessToken
        OAuth2AccessToken accessToken = this.createAccessToken(authentication, refreshToken);
        this.tokenStore.storeAccessToken(accessToken, authentication);
        refreshToken = accessToken.getRefreshToken();
        if (refreshToken != null) {
            this.tokenStore.storeRefreshToken(refreshToken, authentication);
        }

        return accessToken;
    }
  • 继续进入该类的重载方法createAccessToken
    private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
        DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
        // 判断过期时间
        int validitySeconds = this.getAccessTokenValiditySeconds(authentication.getOAuth2Request());
        if (validitySeconds > 0) {
            token.setExpiration(new Date(System.currentTimeMillis() + (long)validitySeconds * 1000L));
        }
		// 设置RefreshToken和权限
        token.setRefreshToken(refreshToken);
        token.setScope(authentication.getOAuth2Request().getScope());
        // 判断是否有增强器,有则执行增强方法
        return (OAuth2AccessToken)(this.accessTokenEnhancer != null ? this.accessTokenEnhancer.enhance(token, authentication) : token);
    }

附上其他部分的代码

RedisAuthorizationCodeServices

这个类主要继承了RandomValueAuthorizationCodeServices 类,该类和TokenServices的作用差不多,不过一个是生成Token,一个是生成授权码,这里就不多说了

	RandomValueAuthorizationCodeServicesprivate RandomValueStringGenerator generator = new RandomValueStringGenerator();
	//随机生成授权码的规则
    public String createAuthorizationCode(OAuth2Authentication authentication) {
        String code = this.generator.generate();
        this.store(code, authentication);
        return code;
    }
public class RedisAuthorizationCodeServices extends RandomValueAuthorizationCodeServices {

	private RedisTemplate<String,Object> redisTemplate;

	
	public RedisTemplate<String, Object> getRedisTemplate() {
		return redisTemplate;
	}

	public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}

	/**
	 * 替换JdbcAuthorizationCodeServices的存储策略
	 * 将存储code到redis,并设置过期时间,10分钟<br>
	 */
	@Override
	protected void store(String code, OAuth2Authentication authentication) {
		
		redisTemplate.opsForValue().set(redisKey(code), authentication, 10, TimeUnit.MINUTES);
		
		 
	}

	@Override
	protected OAuth2Authentication remove(final String code) {
		 
		String codeKey =redisKey(code) ;
			
		OAuth2Authentication token = (OAuth2Authentication) redisTemplate.opsForValue().get(codeKey) ;
			
		this.redisTemplate.delete(codeKey); 

		return token;
	}

	private String redisKey(String code) {
		return "oauth:code:" + code;
	}
}
RedisClientDetailsService

这个是存储客户端信息,建立数据库连接的类

@Slf4j
@SuppressWarnings("all")
public class RedisClientDetailsService extends JdbcClientDetailsService {
   
    ... 省略查询数据库的语句 ...
    private RedisTemplate<String,Object> redisTemplate;
    
    private final JdbcTemplate jdbcTemplate;
    
    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public RedisClientDetailsService(DataSource dataSource) {
        super(dataSource);
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        setSelectClientDetailsSql(SELECT_CLIENT_DETAILS_SQL) ;
        setFindClientDetailsSql(SELECT_FIND_STATEMENT) ;
    }

   
    @Override
    public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
        ClientDetails clientDetails = null;

        try {
			// 先从redis获取
			String value = (String) redisTemplate.boundHashOps(UaaConstant.CACHE_CLIENT_KEY).get(clientId);
			// 如果缓存中没有 那么先去数据库查询
			if (StringUtils.isBlank(value)) {
			    // 缓存和读取client
			    clientDetails = cacheAndGetClient(clientId);
			} else {
			    // 如果不为空 那么就通过json类进行解析成 类实体
			    clientDetails = JSONObject.parseObject(value, BaseClientDetails.class);
			}
		} catch (Exception e) {
			log.error("clientId:{},{}", clientId, clientId );
            throw new InvalidClientException ("应用获取失败"){};
		}

        return clientDetails;
    }

	private ClientDetails cacheAndGetClient(String clientId) {
        // 从数据库读取
        ClientDetails clientDetails = null ;
        
        try {
    			clientDetails = jdbcTemplate.queryForObject(SELECT_CLIENT_DETAILS_SQL, new ClientDetailsRowMapper(), clientId);

            if (clientDetails != null) {
                // 写入redis缓存
                redisTemplate.boundHashOps(UaaConstant.CACHE_CLIENT_KEY).put(clientId, JSONObject.toJSONString(clientDetails));
                log.info("缓存clientId:{},{}", clientId, clientDetails);
            }
        }catch (EmptyResultDataAccessException e) {
        	log.error("clientId:{},{}", clientId, clientId );
            throw new AuthenticationException ("应用不存在"){};
		} catch (NoSuchClientException e){
        	log.error("clientId:{},{}", clientId, clientId );
            throw new AuthenticationException ("应用不存在"){};
        }catch (InvalidClientException e) {
        	throw new AuthenticationException ("应用状态不合法"){};
        }

        return clientDetails;
    }

    @Override
    public void updateClientDetails(ClientDetails clientDetails) throws NoSuchClientException {
	    // 先更新数据库 然后再更新缓存 那么会得到读到旧缓存问题
        super.updateClientDetails(clientDetails);
        cacheAndGetClient(clientDetails.getClientId());
    }

    @Override
    public void updateClientSecret(String clientId, String secret) throws NoSuchClientException {
        super.updateClientSecret(clientId, secret);
        cacheAndGetClient(clientId);
    }

    @Override
    public void removeClientDetails(String clientId) throws NoSuchClientException {
        super.removeClientDetails(clientId);
        removeRedisCache(clientId);
    }

    private void removeRedisCache(String clientId) {
        redisTemplate.boundHashOps(UaaConstant.CACHE_CLIENT_KEY).delete(clientId);
    }


    public void loadAllClientToCache() {
        if (redisTemplate.hasKey(UaaConstant.CACHE_CLIENT_KEY)) {
            return;
        }
        log.info("将oauth_client_details全表刷入redis");

        List<ClientDetails> list = this.listClientDetails();
        if (CollectionUtils.isEmpty(list)) {
        	log.error("oauth_client_details表数据为空,请检查");
            return;
        }

        list.parallelStream().forEach(client -> {
            redisTemplate.boundHashOps(UaaConstant.CACHE_CLIENT_KEY).put(client.getClientId(), JSONObject.toJSONString(client));
        });
    }
    
    
    public List<ClientDetails> listClientDetails() {
  		return jdbcTemplate.query(SELECT_FIND_STATEMENT,  new ClientDetailsRowMapper());
  	}

  	// 实现一个JDBC 查询的属性 和 类属性的 Mapper映射类
	private static class ClientDetailsRowMapper implements RowMapper<ClientDetails> {
		private com.open.capacity.uaa.server.json.JsonMapper mapper = createJsonMapper();

		// 重写mapRow方法
        @Override
		public ClientDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
			DefaultClientDetails details = new DefaultClientDetails(rs.getString(1), rs.getString(3), rs.getString(4),
					rs.getString(5), rs.getString(7), rs.getString(6));
			details.setClientSecret(rs.getString(2));
			if (rs.getObject(8) != null) {
				details.setAccessTokenValiditySeconds(rs.getInt(8));
			}
			if (rs.getObject(9) != null) {
				details.setRefreshTokenValiditySeconds(rs.getInt(9));
			}
			String json = rs.getString(10);
			if (json != null) {
				try {
					Map<String, Object> additionalInformation = mapper.read(json, Map.class);
					details.setAdditionalInformation(additionalInformation);
				}
				catch (Exception e) {
					log.warn("Could not decode JSON for additional information: " + details, e);
				}
			}
			String scopes = rs.getString(11);
			long ifLimit = rs.getLong(12) ;
			details.setIfLimit(ifLimit);
			long limitCount = rs.getLong(13) ;
			details.setLimitCount(limitCount);
			details.setId( rs.getLong(14));
			if (scopes != null) {
				details.setAutoApproveScopes(org.springframework.util.StringUtils.commaDelimitedListToSet(scopes));
			}
			return details;
		}
	}
	 
	// 建造者模式
	private static com.open.capacity.uaa.server.json.JsonMapper createJsonMapper() {
		if (ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", null)) {
			return new com.open.capacity.uaa.server.json.JacksonMapper();
		}
		else if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", null)) {
			return new com.open.capacity.uaa.server.json.Jackson2Mapper();
		}
		return new com.open.capacity.uaa.server.json.NotSupportedJsonMapper();
	} 
}

总结

  • 结合Oauth2的特性和Spring Security的部分源码的讲解,深入的了解token的创建流程。
  • 主要分为客户端信息的存储 -> 授权码请求的发起 -> 授权码的生成 -> 授权码的存储 -> accessToken和RefreshToken的生成存储 -> 接口验证通过 -> 访问服务
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-09-15 02:20:34  更:2022-09-15 02:21:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:14:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码