一.介绍

Spring Security Oauth2是目前市面上非常流行的实现了OAuth2.0协议的权限框架。本文会介绍其是如何获取token以及刷新token的。

二.AbstractEndPoint

Spring Security OAuth2获取token校验token等接口均配置在EndPoint中的

AuthorizationEndpoint主要是第三方授权模式中的 获取code的流程接口 http://localhost:xxxx/auth/oauth/authorize

TokenEndpoint主要是获取token/刷新token的接口 http://localhost:xxxx/auth/oauth/token

2.1 AuthorizationEndpoint

AuthorizationEndpoint主要是获取第三方授权模式中的code,核心代码如下

@RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)public View approveOrDeny(@RequestParam Map<String, String> approvalParameters, Map<String, ?> model,SessionStatus sessionStatus, Principal principal) {if (!(principal instanceof Authentication)) {sessionStatus.setComplete();throw new InsufficientAuthenticationException("User must be authenticated with Spring Security before authorizing an access token.");}AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");if (authorizationRequest == null) {//清空session缓存sessionStatus.setComplete();throw new InvalidRequestException("Cannot approve uninitialized authorization request.");}try {Set<String> responseTypes = authorizationRequest.getResponseTypes();authorizationRequest.setApprovalParameters(approvalParameters);authorizationRequest = userApprovalHandler.updateAfterApproval(authorizationRequest,(Authentication) principal);boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);authorizationRequest.setApproved(approved);if (authorizationRequest.getRedirectUri() == null) {sessionStatus.setComplete();throw new InvalidRequestException("Cannot approve request when no redirect URI is provided.");}if (!authorizationRequest.isApproved()) {return new RedirectView(getUnsuccessfulRedirect(authorizationRequest,new UserDeniedAuthorizationException("User denied access"), responseTypes.contains("token")),false, true, false);}if (responseTypes.contains("token")) {return getImplicitGrantResponse(authorizationRequest).getView();}//生成code并跳转到传递过来的redirect_uri上return getAuthorizationCodeResponse(authorizationRequest, (Authentication) principal);}finally {sessionStatus.setComplete();}}//生成viewprivate View getAuthorizationCodeResponse(AuthorizationRequest authorizationRequest, Authentication authUser) {try {return new RedirectView(getSuccessfulRedirect(authorizationRequest,generateCode(authorizationRequest, authUser)), false, true, false);}catch (OAuth2Exception e) {return new RedirectView(getUnsuccessfulRedirect(authorizationRequest, e, false), false, true, false);}}//生成codeprivate String generateCode(AuthorizationRequest authorizationRequest, Authentication authentication)throws AuthenticationException {try {OAuth2Request storedOAuth2Request = getOAuth2RequestFactory().createOAuth2Request(authorizationRequest);OAuth2Authentication combinedAuth = new OAuth2Authentication(storedOAuth2Request, authentication);String code = authorizationCodeServices.createAuthorizationCode(combinedAuth);return code;}catch (OAuth2Exception e) {if (authorizationRequest.getState() != null) {e.addAdditionalInformation("state", authorizationRequest.getState());}throw e;}}

2.2 TokenEndPoint

此处是TokenEndPoint的部分代码

 @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParamMap<String, String> parameters) throws HttpRequestMethodNotSupportedException {if (!(principal instanceof Authentication)) {throw new InsufficientAuthenticationException("There is no client authentication. Try adding an appropriate authentication filter.");}//校验clientIdString clientId = getClientId(principal);ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);if (clientId != null && !clientId.equals("")) {// Only validate the client details if a client authenticated during this// request.if (!clientId.equals(tokenRequest.getClientId())) {// double check to make sure that the client ID in the token request is the same as that in the// authenticated clientthrow new InvalidClientException("Given client ID does not match authenticated client");}}if (authenticatedClient != null) {oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);}//校验grantTypeif (!StringUtils.hasText(tokenRequest.getGrantType())) {throw new InvalidRequestException("Missing grant type");}if (tokenRequest.getGrantType().equals("implicit")) {//implicit模式不支持token获取throw new InvalidGrantException("Implicit grant type not supported from token endpoint");}//判断是否是第三方授权模式if (isAuthCodeRequest(parameters)) {// The scope was requested or determined during the authorization stepif (!tokenRequest.getScope().isEmpty()) {logger.debug("Clearing scope of incoming token request");tokenRequest.setScope(Collections.<String> emptySet());}}//判断是否是refresh_token模式if (isRefreshTokenRequest(parameters)) {// A refresh token has its own default scopes, so we should ignore any added by the factory here.tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));}//适配器模式,通过grant_type匹配对应的Granter,创建access_token并返回OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);if (token == null) {throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());}return getResponse(token);}/*** @param principal the currently authentication principal* @return a client id if there is one in the principal*/protected String getClientId(Principal principal) {Authentication client = (Authentication) principal;if (!client.isAuthenticated()) {throw new InsufficientAuthenticationException("The client is not authenticated.");}String clientId = client.getName();if (client instanceof OAuth2Authentication) {// Might be a client and user combined authenticationclientId = ((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<OAuth2AccessToken>(accessToken, headers, HttpStatus.OK);}private boolean isRefreshTokenRequest(Map<String, String> parameters) {return "refresh_token".equals(parameters.get("grant_type")) && parameters.get("refresh_token") != null;}private boolean isAuthCodeRequest(Map<String, String> parameters) {return "authorization_code".equals(parameters.get("grant_type")) && parameters.get("code") != null;}

三.TokenGranter

grant_type的类型有:password(密码模式)、authorization_code(授权码模式)、implicit(隐式模式,不能获取token)、client_credentials(客户端模式)、refresh_token

TokenGranter接口下定义了各种模式,核心方法是grant,执行此方法可以获得OAuth2AccessToken

3.1 CompositeTokenGranter

Spring Security OAuth2默认使用的是CompositeTokenGranter

 private TokenGranter tokenGranter() {//如果tokenGranter没有设置,则默认使用的 CompositeTokenGranter ,但本质上还是使用的AbstractTokenGranter的几个子类if (tokenGranter == null) {tokenGranter = new TokenGranter() {private CompositeTokenGranter delegate;@Overridepublic OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {if (delegate == null) {delegate = new CompositeTokenGranter(getDefaultTokenGranters());}return delegate.grant(grantType, tokenRequest);}};}return tokenGranter;}//获取默认的几种模式,不过只包括第三方授权、efresh_code、客户端、密码这四个模式private List<TokenGranter> getDefaultTokenGranters() {ClientDetailsService clientDetails = clientDetailsService();AuthorizationServerTokenServices tokenServices = tokenServices();AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();OAuth2RequestFactory requestFactory = requestFactory();List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails,requestFactory));tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory);tokenGranters.add(implicit);tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));if (authenticationManager != null) {tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,clientDetails, requestFactory));}return tokenGranters;}

看一下CompositeTokenGranter的代码,这是一个TokenGranter的集合类,他的grant方法是依次判断tokenGranters里的TokenGrantergrant_type哪一个和前端传递的grant_type匹配,如果有匹配的,执行对应的Granter,如果没有,就直接返回Null

public class CompositeTokenGranter implements TokenGranter {private final List<TokenGranter> tokenGranters;public CompositeTokenGranter(List<TokenGranter> tokenGranters) {this.tokenGranters = new ArrayList<TokenGranter>(tokenGranters);}public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {//遍历各个granter并匹配grantTypefor (TokenGranter granter : tokenGranters) {OAuth2AccessToken grant = granter.grant(grantType, tokenRequest);if (grant!=null) {return grant;}}return null;}public void addTokenGranter(TokenGranter tokenGranter) {if (tokenGranter == null) {throw new IllegalArgumentException("Token granter is null");}tokenGranters.add(tokenGranter);}}

3.2 AbstractTokenGranter

这个抽象类是我们上面所说的几种模式的父类,在继承TokenGranter的同时,抽取了各个模式的共有方法和代码

public abstract class AbstractTokenGranter implements TokenGranter {protected final Log logger = LogFactory.getLog(getClass());private final AuthorizationServerTokenServices tokenServices;private final ClientDetailsService clientDetailsService;private final OAuth2RequestFactory requestFactory;private final String grantType;protected AbstractTokenGranter(AuthorizationServerTokenServices tokenServices,ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {this.clientDetailsService = clientDetailsService;this.grantType = grantType;this.tokenServices = tokenServices;this.requestFactory = requestFactory;}//实现tokenGranter接口的方法public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {//匹配grant_typeif (!this.grantType.equals(grantType)) {return null;}String clientId = tokenRequest.getClientId();ClientDetails client = clientDetailsService.loadClientByClientId(clientId);//判断账号权限validateGrantType(grantType, client);if (logger.isDebugEnabled()) {logger.debug("Getting access token for: " + clientId);}//获取access_tokenreturn getAccessToken(client, tokenRequest);}//获取access_token方法,可以看到,此处获取access_token的本质是创建access_token,意味着每次获取都会创建一个新的access_token//tokenServices的解析见下面的 DefaultTokenServicesprotected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {return tokenServices.createAccessToken(getOAuth2Authentication(client, tokenRequest));}protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {OAuth2Request storedOAuth2Request = requestFactory.createOAuth2Request(client, tokenRequest);return new OAuth2Authentication(storedOAuth2Request, null);}protected void validateGrantType(String grantType, ClientDetails clientDetails) {Collection<String> authorizedGrantTypes = clientDetails.getAuthorizedGrantTypes();if (authorizedGrantTypes != null && !authorizedGrantTypes.isEmpty()&& !authorizedGrantTypes.contains(grantType)) {throw new InvalidClientException("Unauthorized grant type: " + grantType);}}protected AuthorizationServerTokenServices getTokenServices() {return tokenServices;}protected OAuth2RequestFactory getRequestFactory() {return requestFactory;}}

3.3 DefaultTokenServices

在分析具体的几个模式的代码之前,我们需要先来看看AuthorizationServerTokenServices的子类DefaultTokenServices,这个是其默认实现(这里只贴出其createAccessToken方法和createRefreshToken的代码)

public class DefaultTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices,ConsumerTokenServices, InitializingBean {private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days.private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours.//判断是否支持refreshTokenprivate boolean supportRefreshToken = false;//刷新(即重新生成)access_token时,是否需要刷新(即重新生成)refresh_tokenprivate boolean reuseRefreshToken = true;private TokenStore tokenStore;private ClientDetailsService clientDetailsService;private TokenEnhancer accessTokenEnhancer;private AuthenticationManager authenticationManager;/*** Initialize these token services. If no random generator is set, one will be created.*/public void afterPropertiesSet() throws Exception {Assert.notNull(tokenStore, "tokenStore must be set");}@Transactionalpublic OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {//取出存储的access_tokenOAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);OAuth2RefreshToken refreshToken = null;if (existingAccessToken != null) {if (existingAccessToken.isExpired()) {//存储的有access_token但已过期,从存储中删除此access_token,如果此access_token有对应的refresh_token,则从存储中删除此refresh_tokenif (existingAccessToken.getRefreshToken() != null) {refreshToken = existingAccessToken.getRefreshToken();// The token store could remove the refresh token when the// access token is removed, but we want to// be sure...tokenStore.removeRefreshToken(refreshToken);}tokenStore.removeAccessToken(existingAccessToken);}else {// Re-store the access token in case the authentication has changed//存储的有access_token,并且未过期,存储后直接返回即可tokenStore.storeAccessToken(existingAccessToken, authentication);return existingAccessToken;}}// Only create a new refresh token if there wasn't an existing one// associated with an expired access token.// Clients might be holding existing refresh tokens, so we re-use it in// the case that the old access token// expired.if (refreshToken == null) {refreshToken = createRefreshToken(authentication);}// But the refresh token itself might need to be re-issued if it has// expired.else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {//如果refresh_token已过期,创建新的refreshToken = createRefreshToken(authentication);}}OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);tokenStore.storeAccessToken(accessToken, authentication);// In case it was modifiedrefreshToken = accessToken.getRefreshToken();if (refreshToken != null) {tokenStore.storeRefreshToken(refreshToken, authentication);}return accessToken;}@Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)throws AuthenticationException {//如果不支持刷新token,则直接报错if (!supportRefreshToken) {throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);}OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);if (refreshToken == null) {throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);}OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);if (this.authenticationManager != null && !authentication.isClientOnly()) {// The client has already been authenticated, but the user authentication might be old now, so give it a// chance to re-authenticate.Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());user = authenticationManager.authenticate(user);Object details = authentication.getDetails();authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);authentication.setDetails(details);}String clientId = authentication.getOAuth2Request().getClientId();if (clientId == null || !clientId.equals(tokenRequest.getClientId())) {throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);}// clear out any access tokens already associated with the refresh// token.tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);if (isExpired(refreshToken)) {tokenStore.removeRefreshToken(refreshToken);throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);}authentication = createRefreshedAuthentication(authentication, tokenRequest);//设定的规则是刷新access_token时一起刷新refresh_tokenif (!reuseRefreshToken) {tokenStore.removeRefreshToken(refreshToken);refreshToken = createRefreshToken(authentication);}OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);tokenStore.storeAccessToken(accessToken, authentication);if (!reuseRefreshToken) {tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);}return accessToken;}}

3.4 AuthorizationCodeTokenGranter

这个类是继承了AbstractTokenGranter的,其并没有重写父类的grant方法,只是重写了getAccessToken方法中获取用户信息的方法,即getOAuth2Authentication方法,因为authorization_code模式需要校验重定向uricode

public class AuthorizationCodeTokenGranter extends AbstractTokenGranter {private static final String GRANT_TYPE = "authorization_code";private final AuthorizationCodeServices authorizationCodeServices;public AuthorizationCodeTokenGranter(AuthorizationServerTokenServices tokenServices,AuthorizationCodeServices authorizationCodeServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {this(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory, GRANT_TYPE);}protected AuthorizationCodeTokenGranter(AuthorizationServerTokenServices tokenServices, AuthorizationCodeServices authorizationCodeServices,ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {super(tokenServices, clientDetailsService, requestFactory, grantType);this.authorizationCodeServices = authorizationCodeServices;}@Overrideprotected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {Map<String, String> parameters = tokenRequest.getRequestParameters();//获取codeString authorizationCode = parameters.get("code");String redirectUri = parameters.get(OAuth2Utils.REDIRECT_URI);if (authorizationCode == null) {throw new InvalidRequestException("An authorization code must be supplied.");}//删除并判断code是否合法(这也是为什么code只能使用一次的原因)OAuth2Authentication storedAuth = authorizationCodeServices.consumeAuthorizationCode(authorizationCode);if (storedAuth == null) {throw new InvalidGrantException("Invalid authorization code: " + authorizationCode);}OAuth2Request pendingOAuth2Request = storedAuth.getOAuth2Request();// https://jira.springsource.org/browse/SECOAUTH-333// This might be null, if the authorization was done without the redirect_uri parameterString redirectUriApprovalParameter = pendingOAuth2Request.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);//校验重定向uri是否跟获取code时传递的一样。if ((redirectUri != null || redirectUriApprovalParameter != null)&& !pendingOAuth2Request.getRedirectUri().equals(redirectUri)) {throw new RedirectMismatchException("Redirect URI mismatch.");}//校验clientIdString pendingClientId = pendingOAuth2Request.getClientId();String clientId = tokenRequest.getClientId();if (clientId != null && !clientId.equals(pendingClientId)) {// just a sanity check.throw new InvalidClientException("Client ID mismatch");}// Secret is not required in the authorization request, so it won't be available// in the pendingAuthorizationRequest. We do want to check that a secret is provided// in the token request, but that happens elsewhere.//封装用户信息并存储、返回Map<String, String> combinedParameters = new HashMap<String, String>(pendingOAuth2Request.getRequestParameters());// Combine the parameters adding the new ones last so they override if there are any clashescombinedParameters.putAll(parameters);// Make a new stored request with the combined parametersOAuth2Request finalStoredOAuth2Request = pendingOAuth2Request.createOAuth2Request(combinedParameters);Authentication userAuth = storedAuth.getUserAuthentication();return new OAuth2Authentication(finalStoredOAuth2Request, userAuth);}}

3.5 ResourceOwnerPasswordTokenGranter

密码模式

public class ResourceOwnerPasswordTokenGranter extends AbstractTokenGranter {private static final String GRANT_TYPE = "password";private final AuthenticationManager authenticationManager;public ResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager,AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);}protected ResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {super(tokenServices, clientDetailsService, requestFactory, grantType);this.authenticationManager = authenticationManager;}@Overrideprotected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());String username = parameters.get("username");String password = parameters.get("password");// Protect from downstream leaks of passwordparameters.remove("password");Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);((AbstractAuthenticationToken) userAuth).setDetails(parameters);try {//根据用户名和密码获取用户信息(带校验功能)userAuth = authenticationManager.authenticate(userAuth);}catch (AccountStatusException ase) {//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)throw new InvalidGrantException(ase.getMessage());}catch (BadCredentialsException e) {// If the username/password are wrong the spec says we should send 400/invalid grantthrow new InvalidGrantException(e.getMessage());}if (userAuth == null || !userAuth.isAuthenticated()) {throw new InvalidGrantException("Could not authenticate user: " + username);}OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);       return new OAuth2Authentication(storedOAuth2Request, userAuth);}
}

四.总结

通过阅读Spring Security OAuth2的源码我们发现

  1. Spring Security OAuth2的获取token部分主要是使用适配器模式,通过接口传递的grant_type来判断具体的模式,通过抽象类的公有方法及子类的覆盖方法完成token的获取与刷新
  2. Spring Security OAuth2token获取与刷新是同一个接口,刷新时,refresh_token可以配置成一起刷新或不刷新。如果refresh_token会在access_token刷新时一起刷新的话,那么就可以实现如果用户持续访问系统,便可以一直保持登录,无须再次登录的功能
  3. Spring Security OAth2token刷新策略实质上是重新生成新的token返回,而不是我们理解的把原来的token的过期时长进行延长

Spring Security OAuth2.0 token生成与刷新机制源码阅读相关推荐

  1. Spring Security系列(32)- Spring Security Oauth2之authorities授权使用详解及源码分析

    前言 在oauth_client_details表中,有一个authorities字段,从字面上来看是授权的意思,在之前我们分析了可以通过resourceId和scope进行授权,那么这个author ...

  2. Spring Security OAuth2.0认证授权知识概括

    Spring Security OAuth2.0认证授权知识概括 安全框架基本概念 基于Session的认证方式 Spring Security简介 SpringSecurity详解 分布式系统认证方 ...

  3. Spring Security + OAuth2.0

    授权服务器 授权服务器中有4个端点.说明如下: Authorize Endpoint :授权端点,进行授权. Token Endpoint :令牌端点,经过授权拿到对应的Token. lntrospe ...

  4. Spring Security OAuth2.0认证授权五:用户信息扩展到jwt

    历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] [Spring Securi ...

  5. Spring Security OAuth2.0认证授权三:使用JWT令牌

    历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] 前面两篇文章详细讲解了如何基 ...

  6. spring security oauth2.0 实现

    oauth应该属于security的一部分.关于oauth的的相关知识可以查看阮一峰的文章:http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html ...

  7. Spring Security oauth2.0微信小程序登录

    微信小程序前期开发准备,可以参考这篇文章微信小程序前期准备 1.学习过Spring Secrity oauth2.0的都知道,他有四种登录模式可以选择 authorization code(授权码模式 ...

  8. 从零开始超详细的Spring Security OAuth2.0实现分布式系统授权(注册中心+网关+认证授权服务(JWT令牌验证)+资源调用服务)

    文章目录 一.OAuth2.0 1.介绍 2.例子 3.执行流程 二.Spring Cloud Security OAuth2 1.环境介绍 2.认证流程 三.整合分布式项目 1.技术方案 2.项目结 ...

  9. Spring Security OAuth2.0认证授权

    文章目录 1.基本概念 1.1.什么是认证 1.2 什么是会话 1.3什么是授权 1.4授权的数据模型 1.4 RBAC 1.4.1 基于角色的访问控制 2.基于Session的认证方式 3.整合案例 ...

最新文章

  1. WOJ 1313 - K尾相等数
  2. java字符串 n换行符_java切割字符串中的回车应注意是\n\r不是\n
  3. 2021-02-21 Python Easyocr 图片文字识别
  4. WinForm中的各种对话框
  5. 沙洋有几个微服务群_集群 分布式 微服务
  6. 转:如何在 LoadRunner 脚本中做关联 (Correlation)
  7. Java JUC工具类--ForkJoin
  8. Arcgis Javascript那些事儿(四)--feature access服务编辑feature本质
  9. 紧急!Log4j2 再再爆雷:刚升级,又连爆 “核弹级” 远程数据泄露 ! v2.17.0 横空出世。。。...
  10. jxls设置隐藏列隐藏行
  11. 解决quartus与modelsim-se以及modelsim-altera同时使用导致的仿真失败问题
  12. 增强现实将为我们展示美好的未来还是使我们盲目
  13. 《初等数论及其应用》第三章 素数和最大公因子
  14. Mac 系统升级ssh报错
  15. 处nm是什么意思_“nm”是什么意思啊?
  16. 中国电子学会2022年12月份青少年软件编程Python等级考试试卷三级真题(含答案)
  17. 游戏产业迎新机遇,KuPlay平台助力多元化发展
  18. week6 day4 并发编程之多线程 理论
  19. camera调试名词及问题策略
  20. uniapp引用外部在线js

热门文章

  1. Java EE与jakarta
  2. nmcli-3种网卡绑定方式
  3. java存在溢出攻击吗_缓冲区溢出攻击
  4. LabVIEW 通用框架,满足绝大部分上位机软件功能要求,简单直观
  5. js下载文件,javascript下载文件,FileSaver.js,页面元素保存成文件
  6. LabVIEW学习笔记(三)——程序结构
  7. 我可以使用哪些“ clearfix”方法?
  8. 项目启动找不到placeholder
  9. ShellExecuteEx完美封装,程序提权执行,脚本vbs、bat、exe均可执行
  10. GPS 入门 10 ——GPS快速定位之AGPS、EPO