扩展SpringSecurity OAuth Server以支持多种登录方式,如短信、社交媒体或第三方token

spring cloud启动、token 项目启动顺序 eureka 注册中心 config 配置中心 auth 验证中心 gateway 服务网关 upms-service 权限管理 ................ 阅读详情

类似的介绍有不少,这里总结一下,作为备忘,也从不同的角度进行描述一下,以共用得着的同学参考。

框架原有登录、token生成过程 

  1. client(app,网页等)发出post 请求,到auth server 的/oauth/token 端点,对应org.springframework.security.oauth2.provider.endpoint.TokenEndpoint类中的postAccessToken()方法;
  2. 被ClientCredentialTokenEndpointFilter拦截处理,封装请求中的登录信息为AuthenticationToken,调用attemptAuthentication() 进行验证;
  3. AuthenticationManager接管认证操作,查找匹配的AuthenticationProvider,调用其authenticate() 方法进行具体的认证处理: 如果通过,返回进一步丰富内容的AuthenticationToken,否则抛出异常;
  4. TokenEndpoint.postAccessToken() 如果拿到通过验证的AuthenticationToken,再进一步验证(scope,grantz_type)会生成TokenRequest,然后调用TokenGranter进行OAuth2AccessToken的生成,最后返回给调用的client。

扩展方案

当然可以扩展TokenEndpoint,以处理登录请求中不同的登录信息。但这种方式有些侵入性,不如下面这种方式内聚性好。那就是:

增加一套如下的类:

  1. AuthenticationFilter:用于拦截扩展的登录请求地址(例如:/oauth/extoken),并初步封装请求中的登录信息,例如电话号码,或者其他社交媒体的userid,或者其他应用系统的token(当然要有对应的验证、解析方法);
  2. AuthenticationToken:用于封装上述Filter中提取到的登录信息;
  3. AuthenticationProvider:用于验证上述自定义的AuthenticationToken中封装的信息,并进一步填充后续token获取所需要的信息;
  4. LoginSuccessHandler:用于替代TokenEndpoint中,进行token生成的操作(因为框架原有的TokenEndpoint.postAccessToken()不再执行);
  5. AuthenticationConfigurer:用于将上述功能配置到系统中。

代码片段

本文并未给出所用到的clientDetailService,和userDetailService,相信读者应该已经有了对应的实现,或者简单做一个inMemory的实现也很简单。

  1. Filter
    package stoney.exer.spring.authsvr.exauth;
    
    import lombok.Getter;
    import lombok.Setter;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.security.authentication.AbstractAuthenticationToken;
    import org.springframework.security.authentication.AuthenticationEventPublisher;
    import org.springframework.security.authentication.AuthenticationServiceException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
    import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * 一个通用的外部授权Filter。生成Bean时,可以指定路径,和请求参数 key
     */
    @Slf4j
    
    public class Ex1AuthenticationFilter  extends AbstractAuthenticationProcessingFilter {
        @Setter@Getter
        private AuthenticationEventPublisher eventPublisher;
        private String exTokenKey ;
    
        private boolean postOnly = true;
    
    
        /**
         * 
         * @param authPath 请求登录的路径
         * @param exTokenKey 约定的Header中的token key
         */
        public Ex1AuthenticationFilter(String authPath,String exTokenKey) {
            super(new AntPathRequestMatcher(authPath, "POST"));
            this.exTokenKey = exTokenKey;
        }
    
        @Override
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
                throws AuthenticationException, IOException, ServletException {
            if (postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException(
                        "Authentication method not supported: " + request.getMethod());
            }
    
            String extoken = obtainToken(request);
            if (extoken == null) {
                extoken = "";
            }
            extoken = extoken.trim();
    
            AbstractAuthenticationToken authRequest = new Ex1AuthenticationToken(extoken,extoken , null);
    
            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);
    
            Authentication authResult = null;
            try {
                authResult = this.getAuthenticationManager().authenticate(authRequest);
    
                log.debug("Authentication success: " + authResult);
                SecurityContextHolder.getContext().setAuthentication(authResult);
    
            } catch (Exception failed) {
                SecurityContextHolder.clearContext();
                log.debug("Authentication request failed: " + failed);
    
            }
            return authResult;
        }
    
        protected String obtainToken(HttpServletRequest request) {
            String token = request.getParameter(exTokenKey);
            if (token == null ) {
                token = request.getHeader(exTokenKey);
            }
    
            return token;
        }
    
    
        protected void setDetails(HttpServletRequest request,
                                  AbstractAuthenticationToken authRequest) {
            authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
        }
    
    }

     

  2. Token
    package stoney.exer.spring.authsvr.exauth;
    
    import lombok.Getter;
    import lombok.Setter;
    import lombok.SneakyThrows;
    import org.springframework.security.authentication.AbstractAuthenticationToken;
    import org.springframework.security.core.GrantedAuthority;
    
    import java.util.Collection;
    
    
    //若标记为@Transient暂态,HttpSessionSecurityContextRepository 将不会存储这个Authentication;
    // 若在sessionCreationPolicy 为 STATELESS 时,也可以不标记
    //@Transient
    @Getter @Setter
    public class Ex1AuthenticationToken extends AbstractAuthenticationToken {
        private static final long serialVersionUID = -2826984876386002624L;
    
        protected  Object principal;
        protected String exToken;
    
        public Ex1AuthenticationToken(Collection<? extends GrantedAuthority> authorities) {
            super(authorities);
            super.setAuthenticated(false);
        }
    
        public Ex1AuthenticationToken(Object principal, String exToken,
                                      Collection<? extends GrantedAuthority> authorities) {
            super(authorities);
            this.principal = principal;
            this.exToken = exToken;
            super.setAuthenticated(true);
        }
        @Override
        @SneakyThrows
        public void setAuthenticated(boolean isAuthenticated) {
            if (isAuthenticated) {
                throw new IllegalArgumentException(
                        "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
            }
    
            super.setAuthenticated(false);
        }
        @Override
        public Object getCredentials() {
            return null;
        }
    
        @Override
        public Object getPrincipal() {
            return principal;
        }
    
        @Override
        public void eraseCredentials() {
            super.eraseCredentials();
        }
    }
    

     

  3. Provider
    package stoney.exer.spring.authsvr.exauth;
    
    import lombok.Getter;
    import lombok.Setter;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.support.MessageSourceAccessor;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.SpringSecurityMessageSource;
    import org.springframework.security.core.userdetails.UserDetails;
    import stoney.exer.spring.authsvr.service.MemoryUserService;
    
    @Slf4j
    @Getter @Setter
    public class Ex1AuthenticationProvider implements AuthenticationProvider {
        private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
        @Autowired
        private MemoryUserService userDetailsService;
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            // TODO: 如果是针对外部token,此处要做token解析以得到对应的本系统用户信息;或者是电话号码,此处要做短信码验证;或者是微信openid,此处要做绑定关系查询
            Ex1AuthenticationToken exAuthToken = (Ex1AuthenticationToken)authentication;
            String exToken = exAuthToken.getExToken();
            // 假设解析出来的uid已经设置为用户信息
            String principal = authentication.getPrincipal().toString();
            //TODO: 假设解析出来的用户就是user+token。此处一定要改为自己的用户获取逻辑!!!
            principal = "user"+exToken;
            UserDetails userDetails = userDetailsService.loadUserByUsername(principal);
            if (userDetails == null) {
                log.debug("Authentication failed: no credentials provided");
    
                throw new BadCredentialsException(messages.getMessage(
                        "AbstractUserDetailsAuthenticationProvider.noopBindAccount",
                        "没有绑定账号:"+principal));
            }
    
            // 检查账号状态:锁定,禁用等等
    //        detailsChecker.check(userDetails);
    
            // 填充userDetails 和 Authorities
            Ex1AuthenticationToken authenticationToken = new Ex1AuthenticationToken(userDetails.getUsername(), exToken,
                    userDetails.getAuthorities()); // 如果对应本系统正常用户,可以在此提取权限信息
            authenticationToken.setDetails(authenticationToken.getDetails());
            return authenticationToken;
        }
    
        @Override
        public boolean supports(Class<?> aClass) {
            return  Ex1AuthenticationToken.class.isAssignableFrom(aClass);
        }
    }
    

     

  4. Handler
    package stoney.exer.spring.authsvr.exauth;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.Builder;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.MediaType;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.oauth2.common.OAuth2AccessToken;
    import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
    import org.springframework.security.oauth2.provider.*;
    import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
    import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.stereotype.Component;
    import stoney.exer.common.auth.AuthUtils;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.HashMap;
    
    
    @Builder
    //@Component
    @Slf4j
    public class Ex1LoginSuccessHandler implements AuthenticationSuccessHandler {
        private static final String BASIC_ = "Basic ";
        private ObjectMapper objectMapper;
        private PasswordEncoder passwordEncoder;
        private ClientDetailsService clientDetailsService;
        private AuthorizationServerTokenServices defaultAuthorizationServerTokenServices;
    
        /**
         * Called when a user has been successfully authenticated.
         * 调用spring security oauth API 生成 oAuth2AccessToken
         * 如果不进行 生成 oAuth2AccessToken这一步,认证虽然可以成功(在有Session的情况下,会保存SessionID),
         * 但无法返回token
         *
         * @param request        the request which caused the successful authentication
         * @param response       the response
         * @param authentication the <tt>Authentication</tt> object which was created during
         */
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
            log.debug("Authentiation Success event!");
            String header = request.getHeader(HttpHeaders.AUTHORIZATION);
    
    //        if(true) return;
    
            if (header == null || !header.startsWith(BASIC_)) {
                throw new UnapprovedClientAuthenticationException("请求头中client信息为空");
            }
    
            try {
                String[] tokens = AuthUtils.extractAndDecodeHeader(header);
                assert tokens.length == 2;
                String clientId = tokens[0];
    
                ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
    
    
                TokenRequest tokenRequest = new TokenRequest(new HashMap<>(), clientId, clientDetails.getScope(), "external");
    
                //校验scope
                new DefaultOAuth2RequestValidator().validateScope(tokenRequest, clientDetails);
                OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
                OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
                OAuth2AccessToken oAuth2AccessToken = defaultAuthorizationServerTokenServices.createAccessToken(oAuth2Authentication);
                log.info(" 获取token 成功:{}", oAuth2AccessToken.getValue());
    
                response.setCharacterEncoding("UTF-8");
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);  // APPLICATION_JSON_UTF8_VALUE is deprecated
                PrintWriter printWriter = response.getWriter();
                printWriter.append(objectMapper.writeValueAsString(oAuth2AccessToken));
            } catch (
                    IOException e) {
                throw new BadCredentialsException(
                        "Failed to decode basic authentication token");
            }
            clearAuthenticationAttributes(request);
    
        }
        protected final void clearAuthenticationAttributes(HttpServletRequest request) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                session.removeAttribute("SPRING_SECURITY_LAST_EXCEPTION");
            }
        }
    }
    

     

  5. Configurer
    package stoney.exer.spring.authsvr.exauth;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.Getter;
    import lombok.Setter;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.security.authentication.AuthenticationEventPublisher;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.oauth2.provider.ClientDetailsService;
    import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
    import org.springframework.security.web.DefaultSecurityFilterChain;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import stoney.exer.spring.authsvr.service.MemoryUserService;
    
    @Setter @Getter
    @Slf4j
    public class Ex1AuthenticationConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
        @Autowired // 来自 MemoryUserService 的@Component
        private MemoryUserService userDetailsService;
        @Autowired // 来自框架
        private AuthenticationEventPublisher defaultAuthenticationEventPublisher;
        @Lazy @Autowired // 来自本文件 @Bean定义
        private AuthenticationSuccessHandler ex1LoginSuccessHandler;
    
        @Override
        public void configure(HttpSecurity http)  throws Exception {
            log.debug("Configuring Ex1AuthenticationFilter");
            Ex1AuthenticationFilter ex1AuthenticationFilter = new Ex1AuthenticationFilter("/ex1token","ex1token");
    
            ex1AuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
    //        ex1AuthenticationFilter.setEventPublisher(defaultAuthenticationEventPublisher);
    //        ExtAuthenticationFilter.setAuthenticationEntryPoint(new ResourceAuthExceptionEntryPoint(objectMapper));
            ex1AuthenticationFilter.setAuthenticationSuccessHandler(ex1LoginSuccessHandler);
            log.debug("LoginSuccessHandler for {} is {}",ex1AuthenticationFilter,ex1LoginSuccessHandler);
    
            Ex1AuthenticationProvider ex1AuthenticationProvider = new Ex1AuthenticationProvider();
            ex1AuthenticationProvider.setUserDetailsService(userDetailsService);
            log.debug("UserDetailService for {} is {}",ex1AuthenticationProvider,userDetailsService);
    
            http
                   
                    // 加入 AuthenticationProvider 和 AuthenticationFilter
                    .authenticationProvider(ex1AuthenticationProvider)
                    .addFilterBefore(ex1AuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
            ;
    
            
        }
    
        @Bean("ex1LoginSuccessHandler")
        public Ex1LoginSuccessHandler ex1LoginSuccessHandler(
                @Autowired ObjectMapper objectMapper, // 来自框架
                @Autowired ClientDetailsService clientDetailsService, // 来自 ??
                @Autowired PasswordEncoder passwordEncoder, // 来自SecurityConfig @Bean 定义
                @Lazy @Autowired AuthorizationServerTokenServices defaultAuthorizationServerTokenServices) // 来自框架
        {
            log.debug("~~~ ex1LoginSuccessHandler Bean init info:\n\tObject Mapper: {},\n\tclientDetailService: {}," +
                            "\n\tPasswordEncoder: {},\n\tAuthorizationServerTokenServices: {}",
                    objectMapper,clientDetailsService,passwordEncoder,defaultAuthorizationServerTokenServices);
            return Ex1LoginSuccessHandler.builder()
                    .objectMapper(objectMapper)
                    .clientDetailsService(clientDetailsService)
                    .passwordEncoder(passwordEncoder)
                    .defaultAuthorizationServerTokenServices(defaultAuthorizationServerTokenServices).build();
        }
    }
    

     

  6. 其他工具类
    package stoney.exer.common.auth;
    
    
    import lombok.SneakyThrows;
    import lombok.experimental.UtilityClass;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.http.HttpHeaders;
    
    import javax.servlet.http.HttpServletRequest;
    import java.nio.charset.StandardCharsets;
    import java.util.Base64;
    
    /**
     * 认证授权相关工具类
     */
    @Slf4j
    @UtilityClass
    public class AuthUtils {
    	private final String BASIC_ = "Basic ";
    
    	/**
    	 * 从header 请求中的clientId/clientsecect
    	 *
    	 * @param header header中的参数
    	 * @throws RuntimeException if the Basic header is not present or is not valid
    	 *                          Base64
    	 */
    	@SneakyThrows
    	public String[] extractAndDecodeHeader(String header) {
    
    		byte[] base64Token = header.substring(6).getBytes("UTF-8");
    		byte[] decoded;
    		try {
    			decoded = Base64.getUrlDecoder().decode(base64Token);
    		} catch (IllegalArgumentException e) {
    			throw new RuntimeException(
    					"Failed to decode basic authentication token");
    		}
    
    		String token = new String(decoded, StandardCharsets.UTF_8);
    
    		int delim = token.indexOf(":");
    
    		if (delim == -1) {
    			throw new RuntimeException("Invalid basic authentication token");
    		}
    		return new String[]{token.substring(0, delim), token.substring(delim + 1)};
    	}
    
    	/**
    	 * *从header 请求中的clientId/clientsecect
    	 *
    	 * @param request
    	 * @return
    	 */
    	@SneakyThrows
    	public String[] extractAndDecodeHeader(HttpServletRequest request) {
    		String header = request.getHeader(HttpHeaders.AUTHORIZATION);
    
    		if (header == null || !header.startsWith(BASIC_)) {
    			throw new RuntimeException("请求头中client信息为空");
    		}
    
    		return extractAndDecodeHeader(header);
    	}
    }
    

     

配置方法

在auth server原有的配置(WebSecurityConfigurerAdapter)中,加入:

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
// TODO: 此处略去原有配置若干行 ...

                // 添加外部token支持的配置。若希望是无状态 token验证(比较严格),应加上SessionCreationPolicy.STATELESS 配置
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .apply(ex1AuthenticationConfigurer);
        ;
}

 @Bean
    public Ex1AuthenticationConfigurer ex1AuthenticationConfigurer()
    {
        Ex1AuthenticationConfigurer ex1AuthenticationConfigurer = new Ex1AuthenticationConfigurer();

        return ex1AuthenticationConfigurer;
    }

注意上述配置中,加入了sessionCreationPolicy(SessionCreationPolicy.STATELESS) 的内容,这是因为如果不这样设置,对应的请求会使用HttpSessionSecurityCHttpSessionSecurityContextRepositoryontextRepository存储session(而这还会在一些框架版本组合中引起tomcat的一个AbstractMethod异常),对于分布式服务环境来说没有必要。这样设置之后,框架会用NullSecurityContextRepository 去代替 HttpSessionSecurityContextRepository, 从而不在session中存储token,而要求每次请求必须带有token,强制验证。

请求方法

curl -X POST -i -u order-client:order-secret-8888 "http://localhost:6081/ex1token" -H "ex1token: 1234567890"

对比之前原有的请求:

curl -X POST -i "http://localhost:6081/oauth/token?grant_type=password&client_id=client1&client_secret=secret1&username=user1&password=123456&scope=all"

 

No1.搭建基本的密码模式请求token(授权服务端) 首先,暂时不搭建cloud相关的环境,暂时不会使用到数据库和redis等库,用户信息、客户端信息、授权信息等都存储在内存中,只编写跟密码模式相关的代码(此处的密码模式是按照自定义代码编写)仅作为最基础的授权认证服务端。 阅读详情

相关推荐

oauth2登录认证用户Principal,获取token

oauth2登录获取token,认证用户Principal,endpoints的映射pathMapping("/oauth/token","/ogin")

qq_40289611的博客 2321

自定义的方式,使用oauth2生成token+token续命

使用oauth2生成 token 的流程+续命

m0_56356631的博客 7412

【Qualcomm高通音频】如何封装音频控件?

音频代码涉及面很广,这篇文档介绍如何封装音频控件给HAL层使用。这里以AW87519芯片调试为例进行介绍封装音频控件和使用的方法。在最初接出到高通音频基础功能调试的时候,都是从tinyalsa指令/mixer_paths文件配置开始。因为单独一个音频设备的调试,都对应一条音频链路的打通。比如以下链接上高通平台基础音频输入输出设备调试: 【Qualcomm音频】Speaker 调试记录http...

crow_ch的博客 1954

Spring Security Oauth2之获取token流程分析

前言 本文主要研究Spring Security Oauth2 token获取流程,token的获取方式有5种,其中授权模式最复杂,故本文以授权模式为基准研究token的获取,废话不多说,先上一张核心源码流程图! TokenEndpoint: 是入口controller,也就是我们请求/oauth/token返回token的接口 ClientDetailsService: 这个就有点类似SpringSecurity中的UserDetailsService,UserDetailsService是读取用户信息

clyu的博客 8113

武汉理工大学 python实验三

请编一个程序,用户在同一行内输入两个整数,代表头和脚的数量,编程计算笼中各有多少只鸡和兔(假设鸡和兔都正常,无残疾),如无解则输出`“Data Error!题目为:今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?年前,《孙子算经》中记载一个有趣的问题:今有雉兔同笼,上有三十五头,下有九十四足,问雉兔各几何?有一座八层宝塔,每一层都有一些琉璃灯,每一层的灯数都是上一层的二倍,已知共有。编写一个宝塔上的琉璃灯的小程序,计算并输出每层各有多少盏琉璃灯。,如果有多个解,则分行输出,每行一个解;

2401_83472225的博客 5949

【AFAC2026金融智能-赛题四】我做了一个疯狂的决定,把Embedding和Reranker全删了

本文介绍了AFAC2026金融智能挑战赛赛道四的解题思路,核心是在500万token的严格限制下实现金融长文本的高效问答。作者通过四步流水线(规划、BM25检索、记忆压缩、LLM推理)优化流程,重点创新在于: 采用零API调用的纯BM25检索,配合多路查询、封面页加权和页面邻接扩展等策略 设计高效的检索算法,从问题中提取专有名词并构建多路查询组合 深入理解评分公式,平衡准确率和token消耗,最终在准确率提升的同时将token消耗从数百万降至百万级 这些创新使方案在严格限制下实现了性能突破,展示了在资源受限

224

自定义Spring Cloud OAuth2中/oauth/token的返回内容格式

自定义Spring Cloud OAuth2中/oauth/token的返回内容格式背景实现原理代码实现相关类关键切面拦截器 背景 在前后端分离的项目中,一般后端返回给前端的格式是一个固定的json格式。在这个前提下,Spring Cloud OAuth2 生成access token的请求/oauth/token的返回内容就需要自定义。 访问/oauth/token示例如下: 原始返回值的格式如下: 我们希望使用我们自己固定的json格式,如下: 实现原理 原理就是通过切面编程实现对/oauth/t

分享技术,传播知识! 4342

silero-vad 训练自己的数据

silero-vad 训练自己的数据

jacke121的专栏 815

SpringSecurity OAuth2 获取Token端点TokenEndpoint、Token授权TokenGranter接口 详解

1、前言   在《授权服务器是如何实现授权的呢?》中,我们可以了解到服务端实现授权的流程,同时知道,当授权端点AuthorizationEndpoint生成授权码时,就会重定向到客户端的请求地址,这个时候,客户端就会拿着授权码再来授权服务器换取对应的Token,这篇内容,我们就详细分析如何使用授权码code换取Token的。在前面文章中,我们可以了解到客户端是通过“/oauth/token”来换取token的,该接口对应TokenEndpoint类的postAccessToken()方法,我们这篇文章就围绕

向心行者 1万+

【通信协议】GATT 与 BLE HID 工作原理详解

详细介绍BLE HID 跟GATT工作原理与两者的交互关系

以实战经验介绍行业知识 843

【微服务】SpringBoot 4.0 新特性整合项目实战应用详解

SpringBoot 4.0 新特性整合项目实战应用详解

congge 4586

security登录详解及token校验

token

weixin_42293526的博客 871

Matlab仿真:均匀面阵方向图、均匀线阵方向图、均匀圆阵方向图

其中,阵列方向图是评价阵列性能的重要参数之一。均匀面阵由若干个相同的阵元构成,阵元间等距排列,阵元的方向图形状为一个圆盘。均匀线阵由若干个相同的阵元构成,阵元间等距排列,阵元的方向图形状为一个矩形。本文将采用Matlab进行仿真,分别实现均匀面阵方向图、均匀线阵方向图和均匀圆阵方向图,并对三种阵列进行比较分析。通过以上代码仿真,我们可以对均匀面阵、均匀线阵和均匀圆阵进行方向图的绘制,进一步分析不同阵列在天线性能方面的差异。Matlab仿真:均匀面阵方向图、均匀线阵方向图、均匀圆阵方向图。

2301_79326857的博客 3436

一个接口优雅的实现 Spring Cloud OAuth2 自定义token返回格式

今天这篇文章就来回答其中一个问题:如何自定义token的返回格式?

java_beautiful的博客 1947

FLUENT 软件中design modeler网格划分后,导出失败:系统显示“在网格文件导出过程中出现异常。请将数据发送给您的技术支持供应商。”...如何解决?

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 1468

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

OAuth2.0实现自定义颁发token

一. 底层原理 通过浏览器/外部服务传入账号密码,以及clientId以及secret申请token,底层实现token颁发的类: org.springframework.security.oauth2.provider.endpoint.TokenEndpoint -- postAccessToken()方法 二. 自定义实现 /** * 生成 oauth2...

doinbb的博客 6137

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Spring Security Oauth2 认证(获取token/刷新token)流程(password模式)

1.本文介绍的认证流程范围 本文主要对从用户发起获取token的请求(/oauth/token),到请求结束返回token中间经过的几个关键点进行说明。 2.认证会用到的相关请求 注:所有请求均为post请求。 获取access_token请求(/oauth/token)  请求所需参数:client_id、client_secret、grant_type、username、passwo...

gangsijay888的博客 1万+

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

SpringOauth2.0源码分析之获取access_token(四)

1.概述 前面三个章节叙述了用户名密码认证方式中客户端用户名密码认证细节。 SpringOauth2.0源码分析之认证流程分析(一) SpringOauth2.0源码分析之 ProviderManager(二) SpringOauth2.0源码分析之客户端认证(三) 本章节主要深入分析access_token的实现细节。整个流程实现细节如下: 整个流程中主要核心分为三大块: 用户的用户名密...

有信仰的蜗牛 7765

token自刷新功能实现记录

登录控制 : Token自动刷新功能,达到续期目的 前言: 为达成公司内部需求 : APP 登录. PC端登录 可以保留用户登录状态, 用户持续使用中,应在用户无感知的情况下自动续期token. 设计流程实现: 摘要说明: token时长例如 = 30min refreshToken时长务必大于token时长,这里我取值 = 60min 这里当token失效时: 后台会在返回数据时给headers中放入过期提醒状态. 前端需要使用响应前置拦截,并判断headers中的参数是否需要更新token

sunny-blog 4385

慕课笔记-Spring Security Oauth开发APP认证框架1

?如果用户通过一些应用如app来登陆,那么就没有session, 如果一定要用session开发,也是可以的,但是 开发繁琐。 安全性和客户体验差 有些前端技术不支持cookie,如小程序 因此当我们的访问者不是浏览器的时候,用另外一种方式来访问 用发令牌的方式来访问资源 Spring Security Oauth源理 绿色是实现好的,要实现的是自定义的认证 在资源上添加filter,实现对...

mingtiandexia的专栏 918

Spring Security OAuth 2.0 发放令牌接口地址自定义

OAuth 2.0 如何获取令牌 以密码模式为例,获取 Token curl --location --request POST 'http://oauth-server/oauth/token' \ --header 'Authorization: Basic dGVzdDp0ZXN0' \ --data-urlencode 'username=adm...

冷冷的博客 2220

Spring Security OAuth2 token 源码处理逻辑

Spring Security OAuth2 token 源码处理逻辑 公司项目使用OAuth2做登录,主要使用Spring Security OAuth2搭建的授权服务器和资源服务器,文档可参考官方指导文档 https://projects.spring.io/spring-security-oauth/docs/oauth2.html,对于accessToken 和refreshToken看下代码中是如何生成和存储的,稍做记录。 调用/oauth/token获取token: 1.用的post方式调用,所

suiyilp的博客 1354

Spring Security OAuth2模块/oauth/token授权接口自定义返回结果

spring security提供了默认的oauth登录授权接口/oauth/token,该接口位于org.springframework.security.oauth2.provider.endpoint包中的TokenEndpoint类。 公司要实现自定义的登录授权接口,且返回值也要实现私有结构,下面大概讲一下怎么改造这个类来实现要求的业务逻辑。 一、利用Spring AOP方式 二、自定义Controller接口实现 简单粗暴,直接把TokenEndpointer类注入到自定义Controller中;

晴天丶博客 5441

springsecurity 获取token流程分析

springsecurity password模式通过端点获取token主要源码段

lucktomcat的博客 4413

token实现登录功能(具体实现)

具体的验证过程,使用服务端的秘钥和指定的加密算法(如HS256)对Header和Payload进行验证签名,得到一个签名结果。此时看这个签名结果和前端传过来的token中的签名是否一样,如果一样就说明这个token是有效,并不是伪造的,此时要进行判断这个token是否过期,这个行为我们可以再创建token时就将其放入的redis当中,并给它设置过期时间。用户在以后的请求中都要将token放入请求当中,服务端会验证Token的有效性,并根据Token中的信息来判断用户是否有权限发起这个请求。

qq_64680177的博客 5976
上一篇: 如何得到最新、权威的全国县级以上行政区划代码
下一篇: 用新版Edge浏览器打开只有Internet Explorer兼容的老网页
空指针1996
博客等级 码龄19年 17粉丝 28原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值