首页 > 其他分享 >基于Oauth2.0开发SSO核心代码

基于Oauth2.0开发SSO核心代码

时间:2022-10-17 12:08:06浏览次数:47  
标签:代码 springframework SSO authorizationRequest org import security Oauth2.0 new

什么是SSO

单点登录系统主要解决统一认证授权的问题,在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。
想要完成单点登录的效果,必须先统一管理用户信息,其他应用系统必须配合完成改造和对接。

什么场景下,需要用到SSO?

较大型的企业,往往存在多套应用系统。各个应用系统都是在企业发展的某个阶段,因业务发展的需求,开发研制而成。每套系统都会有一套自己的用户体系,需要终端用户注册、登录后才能使用。
随着企业的发展,用到的系统随之增多,用户在操作不同的系统时,需要多次登录,而且每个系统的账号都不一样,这对于用户来说很不方便。
于是,设计一套统一的登录认证系统,避免不必要的反复登录。减轻用户操作负担,提高效率,在企业的发展进程中,显得越来越重要。

什么是OAuth 2.0

OAuth(开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容。
OAuth 2.0是OAuth协议的下一版本,但不向后兼容OAuth 1.0。 OAuth 2.0关注客户端开发者的简易性,同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。

为什么需要OAuth 2.0?

上文提到,为了实现SSO,各个应用系统需要配合对接单点登录系统。OAuth2.0提供了一套简单,通用,可扩展的开放认证授权协议。既可以实现企业内部系统的对接,也可以实现企业与第三方外部系统的认证互通。
鉴于OAuth2.0的开放特性,只要遵守OAuth2.0协议,可以大幅降低系统间对接开发的沟通调试成本。

什么是Spring Security OAuth

Spring Security对OAuth的实现,借助SpringSecurity框架在认证授权方面既有的优势,可以让开发者简易地使用OAuth协议。

基于Oauth2.0开发SSO核心代码_redis

image.png

Spring Security OAuth存在的问题

  • 鉴于OAuth协议本身就有较多的概念(4种角色,4种授权模式),使用Spring Security OAuth就需要定义和管理OAuth相关的Bean。
  • 另一个比较大的问题,在于Spring Security OAuth默认基于Session实现,这在微服务场景下并不适用。这也是本系列文章要解决的核心问题。

代码实现

Springsecurity基础配置

  • 声明认证管理器AuthenticationManager,认证阶段的用户身份鉴别使用自定义的UserDetailsService
  • 采用BCryptPasswordEncoder对登录密码进行加密及编码,BCryptPasswordEncoder基于随机盐+密钥对密码进行加密,并通过SHA-256算法进行编码
  • 自定义认证逻辑过滤器,满足登录请求参数个性化,多样化需求
  • 自定义token解析过滤器,解决微服务无状态场景下,Spring Security OAuth无法在Session中获取用户认证信息的问题


package com.codeiy.auth.oauth.config;

import com.codeiy.auth.oauth.filter.AuthenticationProcessingFilter;
import com.codeiy.auth.oauth.filter.TokenAuthenticationFilter;
import com.codeiy.auth.oauth.handler.OAuthFailureHandler;
import com.codeiy.auth.oauth.handler.OAuthSuccessHandler;
import com.codeiy.auth.oauth.handler.SsoLogoutSuccessHandler;
import com.codeiy.auth.oauth.service.UserDetailsServiceImpl;
import com.codeiy.core.constant.AuthConstants;
import com.codeiy.user.client.UserClient;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
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.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.Filter;

/**
* SpringSecurity基础配置
* 声明认证管理器{@link AuthenticationManager},认证阶段的用户身份鉴别使用自定义的{@link UserDetailsService}
* 采用{@link BCryptPasswordEncoder}对登录密码进行加密及编码,{@link BCryptPasswordEncoder}基于随机盐+密钥对密码进行加密,并通过SHA-256算法进行编码
*
* @author [email protected]
*/
@Primary
@Order(90)
@Configuration(proxyBeanMethods = false)
@RequiredArgsConstructor
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
private final UserClient userClient;
/**
* 基于随机盐+密钥对密码进行加密,并通过SHA-256算法进行编码
*/
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

/**
* 自定义身份认证服务
*/
@Override
@Bean
public UserDetailsService userDetailsService() {
UserDetailsServiceImpl userDetailsService = new UserDetailsServiceImpl();
userDetailsService.setUserClient(userClient);
userDetailsService.setPasswordEncoder(passwordEncoder);
return userDetailsService;
}

/**
* 1. 禁用csrf
* 2. 请求会话采用无状态方式
* 3. 自定义登录登出路径
* 4. 自定义登录请求参数{@link UsernamePasswordAuthenticationFilter}
*
* @param http http请求安全相关配置
*/
@Override
@SneakyThrows
protected void configure(HttpSecurity http) {
Filter loginFilter = loginFilter();
Filter tokenAuthenticationFilter = tokenAuthenticationFilter();
http.formLogin()
.loginProcessingUrl(AuthConstants.LOGIN_PROCESSING_URL).permitAll()
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().logout().logoutSuccessHandler(new SsoLogoutSuccessHandler())
.and().authorizeRequests()
.antMatchers(AuthConstants.LOGOUT_URL).permitAll()
.anyRequest().authenticated()
// 登录请求参数除了用户名,密码之外,还有验证码等其他参数,通过过滤器自定义认证逻辑
.and().addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class)
// 解决微服务架构无状态请求场景下,如何识别当前请求所属用户,并绑定到SpringSecurity上下文
.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

}

/**
* 认证管理器
*/
@Bean
@Override
@SneakyThrows
public AuthenticationManager authenticationManagerBean() {
return super.authenticationManagerBean();
}

/**
* 登录密码编码工具
*/
@Bean
public PasswordEncoder passwordEncoder() {
return passwordEncoder;
}

/**
* 自定义登录认证过滤器,满足登录请求参数个性化,多样化需求
*/
private Filter loginFilter() throws Exception {
AuthenticationProcessingFilter loginFilter = new AuthenticationProcessingFilter();
loginFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(AuthConstants.LOGIN_PROCESSING_URL, AuthConstants.METHOD_POST));
loginFilter.setAuthenticationSuccessHandler(new OAuthSuccessHandler());
loginFilter.setAuthenticationFailureHandler(new OAuthFailureHandler());
loginFilter.setAuthenticationManager(authenticationManager());
return loginFilter;
}

/**
* token解析过滤器,解决微服务无状态场景下,Spring Security OAuth无法在Session中获取用户认证信息的问题
*/
private Filter tokenAuthenticationFilter() {
TokenAuthenticationFilter tokenAuthenticationFilter = new TokenAuthenticationFilter();
return tokenAuthenticationFilter;
}
}

OAuth认证服务器配置

  • 通过注解@EnableAuthorizationServer声明认证服务器
  • 基于ClientDetailsServiceImpl自定义应用系统信息管理
  • 基于Redis存储OAuth协议的AccessToken
  • 声明OAuth2.0简化模式AccessToken生成规则
  • 自定义Auth2.0协议确认授权以及认证请求链接


package com.codeiy.auth.oauth.config;

import com.codeiy.auth.oauth.service.AuthenticationCodeServiceImpl;
import com.codeiy.auth.oauth.service.ClientDetailsServiceImpl;
import com.codeiy.core.constant.AuthConstants;
import com.codeiy.user.client.AppClient;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

/**
* 通过注解{@code @EnableAuthorizationServer}声明认证服务器
* 基于{@link ClientDetailsServiceImpl}自定义应用系统信息管理
* 基于Redis存储OAuth协议的AccessToken
* 声明OAuth2.0简化模式AccessToken生成规则
* 自定义Auth2.0协议确认授权以及认证请求链接
*/
@RequiredArgsConstructor
@EnableAuthorizationServer
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final AppClient appClient;
private final AuthenticationManager authenticationManager;
private final RedisConnectionFactory redisConnectionFactory;
private final PasswordEncoder passwordEncoder;

private ClientDetailsServiceImpl clientDetailsService;
private RedisTokenStore redisTokenStore;
private AuthorizationServerTokenServices tokenServices;
private OAuth2RequestFactory oAuth2RequestFactory;
private ImplicitTokenGranter implicitTokenGranter;
private AuthorizationCodeServices authorizationCodeServices;

/**
* 基于Redis存储OAuth协议的AccessToken
*/
@Bean
public TokenStore tokenStore() {
if (redisTokenStore == null) {
redisTokenStore = new RedisTokenStore(redisConnectionFactory);
redisTokenStore.setPrefix(AuthConstants.OAUTH_PREFIX);
}
return redisTokenStore;
}

/**
* 令牌权限范围配置,使用默认的{@link DefaultOAuth2RequestFactory},可匹配到用户的角色。
*/
@Bean
public OAuth2RequestFactory oAuth2RequestFactory() {
if (oAuth2RequestFactory == null) {
oAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsService());
}
return oAuth2RequestFactory;
}

/**
* OAuth2.0简化模式AccessToken生成规则
*/
@Bean
public ImplicitTokenGranter implicitTokenGranter() {
if (implicitTokenGranter == null) {
implicitTokenGranter = new ImplicitTokenGranter(tokenServices(), clientDetailsService(), oAuth2RequestFactory());
}
return implicitTokenGranter;
}

/**
* OAuth2.0授权码模式,自定义授权码的存储方式
*/
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
if (authorizationCodeServices == null) {
authorizationCodeServices = new AuthenticationCodeServiceImpl(redisConnectionFactory);
}
return authorizationCodeServices;
}

/**
* 自定义应用系统信息管理
*/
private ClientDetailsService clientDetailsService() {
if (clientDetailsService == null) {
clientDetailsService = new ClientDetailsServiceImpl();
clientDetailsService.setPasswordEncoder(passwordEncoder);
clientDetailsService.setAppClient(appClient);
}
return clientDetailsService;
}

/**
* 声明AccessToken管理服务
*/
private AuthorizationServerTokenServices tokenServices() {
if (tokenServices == null) {
tokenServices = new DefaultTokenServices();
}
return tokenServices;
}

/**
* 配置应用系统管理,基于{@link ClientDetailsServiceImpl}自定义应用系统信息管理
*/
@Override
@SneakyThrows
public void configure(ClientDetailsServiceConfigurer clients) {
clients.withClientDetails(clientDetailsService());
}

/**
* 配置获取AccessToken请求的访问权限
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.tokenKeyAccess("permitAll()").allowFormAuthenticationForClients().checkTokenAccess("permitAll()");
}

/**
* 自定义Auth2.0协议确认授权以及认证请求链接
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)
.authorizationCodeServices(authorizationCodeServices())
.tokenServices(tokenServices())
.allowedTokenEndpointRequestMethods(HttpMethod.POST)
.pathMapping("/oauth/confirm_access", "/oauth/confirmAccess")
.pathMapping("/oauth/authorize", "/oauth/customAuthorize")
;
}

}

确认授权核心代码


package com.codeiy.auth.oauth.endpoint;

import cn.hutool.core.util.StrUtil;
import com.codeiy.core.util.R;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.*;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.endpoint.DefaultRedirectResolver;
import org.springframework.security.oauth2.provider.endpoint.RedirectResolver;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.util.*;

/**
* OAuth授权自定义端点
*/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/oauth")
public class AuthEndpoint {
private final ClientDetailsService clientDetailsService;
private final ImplicitTokenGranter implicitTokenGranter;
private final AuthorizationCodeServices authorizationCodeServices;
private final OAuth2RequestFactory oAuth2RequestFactory;

private RedirectResolver redirectResolver = new DefaultRedirectResolver();
private OAuth2RequestValidator oauth2RequestValidator = new DefaultOAuth2RequestValidator();


/**
* 授权确认页面
*
* @return
*/
@GetMapping("/confirmAccess")
public R confirmAccess(HttpServletRequest request) {
String clientId = request.getParameter("clientId");
if (StrUtil.isBlank(clientId)) {
return R.failed("clientId不能为空");
}
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
if (clientDetails == null) {
return R.failed("根据clientId查无数据");
}
return R.ok(clientDetails);
}

/**
* 自定义确认授权
*
* @return
*/
@RequestMapping("/customAuthorize")
public R customAuthorize(HttpServletRequest request, @RequestParam Map<String, String> parameters) {
AuthorizationRequest authorizationRequest = oAuth2RequestFactory.createAuthorizationRequest(parameters);
Set<String> responseTypes = authorizationRequest.getResponseTypes();
if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
}
if (authorizationRequest.getClientId() == null) {
throw new InvalidClientException("A client id must be provided");
}

Authentication principal = SecurityContextHolder.getContext().getAuthentication();
if (principal == null || !principal.isAuthenticated()) {
throw new InsufficientAuthenticationException(
"User must be authenticated with Spring Security before authorization can be completed.");
}

ClientDetails client = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId());

// The resolved redirect URI is either the redirect_uri from the parameters or the one from
// clientDetails. Either way we need to store it on the AuthorizationRequest.
String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
if (!StringUtils.hasText(resolvedRedirect)) {
throw new RedirectMismatchException(
"A redirectUri must be either supplied or preconfigured in the ClientDetails");
}
authorizationRequest.setRedirectUri(resolvedRedirect);

// We intentionally only validate the parameters requested by the client (ignoring any data that may have
// been added to the request by the manager).
oauth2RequestValidator.validateScope(authorizationRequest, client);

authorizationRequest.setApproved("true".equals(parameters.get("user_oauth_approval")));

// Validation is all done, so we can check for auto approval...
if (authorizationRequest.isApproved()) {
if (responseTypes.contains("token")) {
ModelAndView mv = getImplicitGrantResponse(authorizationRequest);
}
if (responseTypes.contains("code")) {
String url = getSuccessfulRedirect(authorizationRequest, generateCode(authorizationRequest, principal));
log.info(url);
}
}


String clientId = request.getParameter("clientId");
if (StrUtil.isBlank(clientId)) {
return R.failed("clientId不能为空");
}
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
if (clientDetails == null) {
return R.failed("根据clientId查无数据");
}
return R.ok(clientDetails);
}


private String getSuccessfulRedirect(AuthorizationRequest authorizationRequest, String authorizationCode) {

if (authorizationCode == null) {
throw new IllegalStateException("No authorization code found in the current request scope.");
}

Map<String, String> query = new LinkedHashMap<String, String>();
query.put("code", authorizationCode);

String state = authorizationRequest.getState();
if (state != null) {
query.put("state", state);
}

return append(authorizationRequest.getRedirectUri(), query, null, false);
}


private String generateCode(AuthorizationRequest authorizationRequest, Authentication authentication)
throws AuthenticationException {

try {

OAuth2Request storedOAuth2Request = oAuth2RequestFactory.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;

}
}

private ModelAndView getImplicitGrantResponse(AuthorizationRequest authorizationRequest) {
try {
TokenRequest tokenRequest = oAuth2RequestFactory.createTokenRequest(authorizationRequest, "implicit");
OAuth2Request storedOAuth2Request = oAuth2RequestFactory.createOAuth2Request(authorizationRequest);
OAuth2AccessToken accessToken = getAccessTokenForImplicitGrant(tokenRequest, storedOAuth2Request);
if (accessToken == null) {
throw new UnsupportedResponseTypeException("Unsupported response type: token");
}
return new ModelAndView(new RedirectView(appendAccessToken(authorizationRequest, accessToken), false, true,
false));
} catch (OAuth2Exception e) {
return new ModelAndView(new RedirectView(getUnsuccessfulRedirect(authorizationRequest, e, true), false,
true, false));
}
}

private String appendAccessToken(AuthorizationRequest authorizationRequest, OAuth2AccessToken accessToken) {

Map<String, Object> vars = new LinkedHashMap<String, Object>();
Map<String, String> keys = new HashMap<String, String>();

if (accessToken == null) {
throw new InvalidRequestException("An implicit grant could not be made");
}

vars.put("access_token", accessToken.getValue());
vars.put("token_type", accessToken.getTokenType());
String state = authorizationRequest.getState();

if (state != null) {
vars.put("state", state);
}
Date expiration = accessToken.getExpiration();
if (expiration != null) {
long expires_in = (expiration.getTime() - System.currentTimeMillis()) / 1000;
vars.put("expires_in", expires_in);
}
String originalScope = authorizationRequest.getRequestParameters().get(OAuth2Utils.SCOPE);
if (originalScope == null || !OAuth2Utils.parseParameterList(originalScope).equals(accessToken.getScope())) {
vars.put("scope", OAuth2Utils.formatParameterList(accessToken.getScope()));
}
Map<String, Object> additionalInformation = accessToken.getAdditionalInformation();
for (String key : additionalInformation.keySet()) {
Object value = additionalInformation.get(key);
if (value != null) {
keys.put("extra_" + key, key);
vars.put("extra_" + key, value);
}
}
// Do not include the refresh token (even if there is one)
return append(authorizationRequest.getRedirectUri(), vars, keys, true);
}

private String getUnsuccessfulRedirect(AuthorizationRequest authorizationRequest, OAuth2Exception failure,
boolean fragment) {

if (authorizationRequest == null || authorizationRequest.getRedirectUri() == null) {
// we have no redirect for the user. very sad.
throw new UnapprovedClientAuthenticationException("Authorization failure, and no redirect URI.", failure);
}

Map<String, String> query = new LinkedHashMap<String, String>();

query.put("error", failure.getOAuth2ErrorCode());
query.put("error_description", failure.getMessage());

if (authorizationRequest.getState() != null) {
query.put("state", authorizationRequest.getState());
}

if (failure.getAdditionalInformation() != null) {
for (Map.Entry<String, String> additionalInfo : failure.getAdditionalInformation().entrySet()) {
query.put(additionalInfo.getKey(), additionalInfo.getValue());
}
}

return append(authorizationRequest.getRedirectUri(), null, query, fragment);

}

private String append(String base, Map<String, ?> query, Map<String, String> keys, boolean fragment) {

UriComponentsBuilder template = UriComponentsBuilder.newInstance();
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base);
URI redirectUri;
try {
// assume it's encoded to start with (if it came in over the wire)
redirectUri = builder.build(true).toUri();
} catch (Exception e) {
// ... but allow client registrations to contain hard-coded non-encoded values
redirectUri = builder.build().toUri();
builder = UriComponentsBuilder.fromUri(redirectUri);
}
template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost())
.userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath());

if (fragment) {
StringBuilder values = new StringBuilder();
if (redirectUri.getFragment() != null) {
String append = redirectUri.getFragment();
values.append(append);
}
for (String key : query.keySet()) {
if (values.length() > 0) {
values.append("&");
}
String name = key;
if (keys != null && keys.containsKey(key)) {
name = keys.get(key);
}
values.append(name + "={" + key + "}");
}
if (values.length() > 0) {
template.fragment(values.toString());
}
UriComponents encoded = template.build().expand(query).encode();
builder.fragment(encoded.getFragment());
} else {
for (String key : query.keySet()) {
String name = key;
if (keys != null && keys.containsKey(key)) {
name = keys.get(key);
}
template.queryParam(name, "{" + key + "}");
}
template.fragment(redirectUri.getFragment());
UriComponents encoded = template.build().expand(query).encode();
builder.query(encoded.getQuery());
}

return builder.build().toUriString();

}

private synchronized OAuth2AccessToken getAccessTokenForImplicitGrant(TokenRequest tokenRequest,
OAuth2Request storedOAuth2Request) {
return implicitTokenGranter.grant("implicit", new ImplicitTokenRequest(tokenRequest, storedOAuth2Request));
}
}

标签:代码,springframework,SSO,authorizationRequest,org,import,security,Oauth2.0,new
From: https://blog.51cto.com/u_15144514/5762047

相关文章

  • Java_SE之String类及其源代码剖析
    字符串特性​String​​是常量,其对象一旦创建就无法改变。当使用+​​​拼接字符串时,会生成新的String​​​对象,而不是向原有的String​​​对象追加内容。查......
  • echarts 在IE11上异常不执行代码的问题
    echarts控件在谷歌浏览器一切正常,但是IE11(windows10上的IE)就不行了,调试发现页面直接就整个异常了,在最开始写了个alert(111)都不执行,看样子应该是哪里有语法错误,导致解析的......
  • Java_SE_Lesson_2:多态与static和final关键字
    多态:父类型的引用可以指向子类型的对象。Parentp=newChild;当使用多态方式调用方法时,首先检查父类中是否有sing方法,如果没有则编译错误;如果有,再去调用子类的si......
  • Java_SE_Lesson_3:接口、单例模式、包与访问控制
    接口中所声明的方法都是抽象方法。接口中的方法都是public​的。接口中也可以定义成员变量。接口中的成员变量都是public,final,static的。一个类不能既是f......
  • io流-字节输出流和FileIntputStream介绍、代码演示
    字节输出流InputStream抽象类是表示字节输出流的所有类的超类可以读取字节信息到内存中它定义了自己输入流的基本共性功能方法InputStream是隶属于java.io包下publ......
  • 多线程-线程安全-同步代码块
    线程安全问题产生的原理出现了线程安全问题卖票出现了重复的票图解 线程同步当我们使用多个线程访问同一个资源的时候且多个线程对资源有写的操作就容易出现线......
  • WPF 后台代码实现绑定
    usingSystem.ComponentModel;usingSystem.Windows;usingSystem.Windows.Controls;usingSystem.Windows.Data;namespaceWpfApp2{///<summary>///Ma......
  • 代码块+控制流图+程序切片的学习
    代码基本知识点代码基本块严格的来说,基本块是满足下列条件的一组连续指令代码,程序的执行(控制流)只能从基本块的第一条语句(入口语句)进入,从基本块的最后一条语句离开。int......
  • 本地线上运行python代码
    http://localhost:39093/1、learning.py文件的代码#!/usr/bin/envpython3#-*-coding:utf-8-*-r'''learning.pyAPython3tutorialfromhttp://www.liaox......
  • Node.js躬行记(24)——低代码
    低代码开发平台(LCDP)是无需编码(0代码)或通过少量代码就可以快速生成应用程序的开发平台。让具有不同经验水平的开发人员可以通过图形化的用户界面,通过拖拽组件和模型驱动......