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.0四种授权模式以及Oauth2.0实战 -> 正文阅读

[移动开发]一文读懂Oauth2.0四种授权模式以及Oauth2.0实战

Oauth2.0

首先我们得了解什么是Oauth2.0,简单来说Oauth2.0它是一个授权协议。我们可能会听说过,使用Oauth2.0来实现单点登录,以及第三方登录。那个什么是授权?

举个通俗易懂的例子,就是第三方人员A要想进入B公司的大厦进行业务交流的时候,因为A并不是B公司的员工,出于安全的缘故,所以他不能够自由的出入B公司的大厦。那个A到了B公司前台的时候,A得去前台和B公司前台工作人员说明来意,并且出示邀请(访问)证明,此时B公司前台工作人员就会给你一张临时工牌让你进入大厦。

在这个例子当中,A没有工牌所以是无法进入B公司大厦里进行业务交流,B公司前台给A一张临时工牌,这个操作就相当于授权。

总的来说,OAuth 2.0 这种授权协议,就是保证第三方(软件)只有在获得授权之后,才可以进一步访问授权者的数据。

1、Oauth2.0授权许可机制协议

Oauth2.0具有多种授权许可机制协议:授权码许可机制、客户端凭据机制、资源拥有者凭据机制(密码模式)和隐式许可机制。

在了解授权许可机制协议之前,我们得需要了解在OAuth 2.0 的体系里面有 4 种角色,按照官方的称呼它们分别是资源拥有者、客户端、授权服务和受保护资源。

  • 资源拥有者(可以指拥有资源的用户)
  • 客户端(可以理解为第三方系统/软件)
  • 授权服务(权限校验和授权系统(认证服务中心))
  • 受保护资源(用户在系统上所具有的资源/或者能够访问的资源)

1.1、授权码许可机制

授权码许可机制的参与者:资源拥有者、客户端、授权服务、受保护资源

授权码模式这种场景下的授权,第三方软件可以通过拿到资源拥有者授权后的授权码,以及注册时的 client_id 和 client_secret 来换回访问令牌 token 的值。

在这里插入图片描述

按照上述时序图举个简单的例子,小明使用微信授权方式登录app。

  1. 小明点开手机里面的app,他不想手动输入账号密码登录,而是采用了微信登录。
  2. 点击微信登录按钮,app拉起授权页面。
  3. 微信授权服务器则生成授权页面,用户看见授权页面点击确定按钮进行授权。
  4. 微信授权服务器校验用户身份合法性后生成请求code,点击确认授权后,页面跳转至app页面并携带请求code(授权码)。
  5. app拿到授权码后,携带授权码向授权服务器获取访问令牌access_token。
  6. 拿到access_token后,则携带access_token向受保护资源发起访问。
  7. 校验access_token无误后,受保护资源返回资源数据(个人的身份数据,昵称,地区等信息)。
  8. 成功登录app,小明继续使用app内的功能。

1.1.1、为什么需要生成授权码以及根据授权码获取access_token步骤?

假设从时序图中抹除授权码的流程,那么从第三步,用户点击确定授权,此时资源拥有者与授权服务器就建立起关联,此时,资源拥有者则与第三方软件前端断开关联,界面则会停留在授权界面。然后授权服务器直接把access_token送给第三方软件后端,后端在携带access_token去访问受保护资源。虽然说资源数据已经拿到了,但是如何通知用户呢?因此,得需要建立起用户与第三方软件前端的关联,所以授权服务器生成授权码后重定向到第三方软件前端则是重新建立起用户与第三方软件前端的关联。

既然如此,那么为什么授权服务器不直接重定向传回access_token,首先并不能保证重定向采用的形式是否是https,而且并不是所有的客户端都支持https,所以重定向传回access_token就会增加access_token失窃的风险。虽然access_token需要与client_id,client_secret一起才能够通过授权服务器校验访问到保护资源,但是在安全层面来说,这都是不适合的。在此层面上看,授权码的作用在于access_token不经过用户浏览器, 保护了access_token。

1.1.2、授权码code可以暴露?

1、授权码Authentication code只能用一次,而且会很快超时失效, 使得被截获后难以运用。

2、授权码需要和client id/client secret共同完成认证,才能够获得access_token。就算授权码如果失窃,单凭授权码是无法得到access_token的。

1.1.3、access_token不能暴露在浏览器那么该存放在哪?

重定向传回access_token会使安全保密性要求极高的访问令牌暴露在浏览器,增加访问令牌失窃风险。

刚开始接触Oauth2.0的我也是比较迷,既然access_token不能暴露在浏览器,那么我到底将access_token存放在哪呢?那我前端有如何进行访问那些受保护资源呢?

在我看来,重定向携带的参数在URL上,http协议下重定向传回access_token的形式,是没有经过数据加密的,他会增加令牌失窃的风险。那么关于access_token存放在哪的问题,个人认为通过授权码以及客户端id和secret共同校验后获取的access_token,可以把access_token存放在localStorage中,localStorage虽然是永久存储,但是access_token会有一个有效期,有效期到了之后,即便access_token一直都存在但是有效期过后就无法访问到受保护资源。

1.1.4、sessionStorage和localStorage区别

1、sessionStorage(会话存储)

  • 生命周期:浏览器打开到关闭的过程

  • 大小:5M

  • 保存的位置:浏览器端

// 存储数据
sessionStorage.setItem("name", "nameValue");
// 获取数据
sessionStorage.getItem("name");
// 删除数据
sessionStorage.removeItem("name");
// 删除所有数据
sessionStorage.clear();

2、localStorage(本地存储【永久存储】)

  • 生命周期: 永久,只能人为删除

  • 大小: 5M甚至更大

  • 保存的位置: 浏览器端

// 存储数据
localStorage.setItem("name", "nameValue");
// 获取数据
localStorage.getItem("name");
// 删除数据
localStorage.removeItem("name");

**注意: **不同浏览器无法共享localStorage或sessionStorage中的信息。相同浏览器的不同页面间【相同域名和端口】可以共享相同的 localStorage,但是不同页面或标签页间无法共享sessionStorage的信息。

1.2、资源拥有者凭据机制(密码模式)

客户端凭据机制的参与者:资源拥有者、客户端、授权服务、受保护资源

资源拥有者凭据,顾名思义就是资源拥有者的凭据(账号,密码)。在这场景里面就不存在第三方软件这概念,相当于就是访问系统中的一个子系统,他们之间互相信任。举个例子来说就是,腾讯有许多的游戏,你只需要用qq账号密码就可以登录游戏玩,不需要进行腾讯授权。因为该游戏是腾讯旗下的,他们相互信任的,所以不存在第三方的说法。

时序图:
在这里插入图片描述

1.3、客户端凭据机制

客户端凭据机制的参与者:客户端、授权服务、受保护资源

相当于就是第三方软件访问不需要资源拥有者授权的资源和数据,换句话说在这里客户端也可以看作是资源拥有者。举个例子来说就是第三方软件访问一些公共的服务,譬如说一些地图信息,logo图标等。

这种场景下的授权,便是客户端凭据许可,第三方软件可以直接使用注册时的 client_id 和 client_secret 来换回访问令牌 token 的值。

时序图:
在这里插入图片描述

1.4、隐式许可机制

隐式许可机制的场景适用于没有后端服务的应用,举个例子来说的话就是在浏览器中执行,譬如说JavaScript应用。

在这种情况下,第三方软件对于浏览器就没有任何保密的数据可以隐藏了,也不再需要应用密钥 app_secret 的值了,也不用再通过授权码 code 来换取访问令牌 access_token 的值了。因此,隐式许可授权流程的安全性会降低很多。

这种场景下的授权,第三方软件可以直接使用注册时的 client_id来换回访问令牌 token 的值。

时序图:
在这里插入图片描述

2、Oauth2.0实战

以资源拥有者模式为例,其他模式都可以由该模式演变而来。

2.1、搭建授权服务器:health-oauth-server

pom.xml 添加依赖oauth2

		<!--oauth2-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>

授权服务器配置

@Configuration
//启动授权服务器
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    /**
     * ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),
     * 客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
     *
     * AuthorizationServerEndpointsConfigurer:用来配置令牌(token)的访问端点和令牌服务(tokenservices)。
     *
     * AuthorizationServerSecurityConfigurer:用来配置令牌端点的安全约束.
     */

    /**
     * 用来配置客户端详情服务(ClientDetailsService)
     * 允许客户端自己申请ClientID
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource);
    }


    /**
     * AuthorizationServer配置令牌访问端点
     * <p>
     * 1、配置我们的Token存放方式不是内存、数据库或Redis方式,而是JWT方式。
     * JWT是Json Web Token缩写也就是使用JSON数据格式包装的Token,由.句号把整个JWT分隔为头、数据体、签名三部分。
     * JWT保存Token虽然易于使用但是不是那么安全,一般用于内部,并且需要走HTTPS+配置比较短的失效时间。
     * 2、配置了JWT Token的非对称加密来进行签名
     * 3、配置了一个自定义的Token增强器,把更多信息放入Token中
     * 4、配置了使用JDBC数据库方式来保存用户的授权批准记录
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        //添加自定义 token增强
//        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(),jwtTokenEnhancer()));
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtTokenEnhancer()));
        // 初始化所有的TokenGranter,并且类型为CompositeTokenGranter
        List<TokenGranter> tokenGranters = getDefaultTokenGranters(endpoints);
        endpoints.approvalStore(approvalStore())
                .tokenGranter(new CompositeTokenGranter(tokenGranters))
                .authorizationCodeServices(authorizationCodeServices())
                .tokenStore(tokenStore())
                .tokenEnhancer(tokenEnhancerChain)
                .authenticationManager(authenticationManager);
    }

    /**
     * 用来配置令牌端点的安全约束.
     * 允许ClientSecret明文方式保存并且可以通过表单提交(而不仅仅是Basic Auth方式提交)
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.checkTokenAccess("permitAll()") //默认为 denyAll 表示拒绝所有  这里开放端点为申请令牌的端点 应当为所有人都可以访问
                .allowFormAuthenticationForClients() //表单的形式提交
                .passwordEncoder(bCryptPasswordEncoder); //设置密码需要使用加密器 针对客户端
    }


    /**
     * 使用JDBC数据库方式来保存用户的授权批准记录
     *
     * @return
     */
    @Bean
    public JdbcApprovalStore approvalStore() {
        return new JdbcApprovalStore(dataSource);
    }


    /**
     * 使用JDBC数据库方式来保存授权码
     *
     * @return
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }


    /**
     * 自定义的Token增强器,把更多信息放入Token中
     *
     * @return
     */
    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new CustomTokenEnhancer();
    }


    /**
     * 配置JWT令牌使用非对称加密方式来验证
     *
     * @return
     */
    @Bean
    protected JwtAccessTokenConverter jwtTokenEnhancer() {
        //设置jwt的转换器 必须要有
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(
                new ClassPathResource(jksProperties.getName()),  //设置加密的加载文件
                jksProperties.getStorePassword().toCharArray()); //设置读取秘钥库文件的密码

        KeyPair keyPair = keyStoreKeyFactory.getKeyPair(jksProperties.getAlias()); //设置获取秘钥的密码
//        KeyPair keyPair = keyStoreKeyFactory.getKeyPair("jwt"); //设置获取秘钥的密码
        //设置秘钥对象
        converter.setKeyPair(keyPair);
        return converter;
    }

    /**
     * 使用JWT令牌存储
     *
     * @return
     */
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtTokenEnhancer());
    }

    /**
     * 初始化所有的TokenGranter
     */
    private List<TokenGranter> getDefaultTokenGranters(AuthorizationServerEndpointsConfigurer endpoints) {

        ClientDetailsService clientDetails = endpoints.getClientDetailsService();
        AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices();
        AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices();
        OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory();

        List<TokenGranter> tokenGranters = new ArrayList<>();
        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));
            tokenGranters.add(new SmsCodeGranter(authenticationManager, endpoints.getTokenServices(),
                    endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), userMapper));
        }
        return tokenGranters;
    }
}

token增强类

public class CustomTokenEnhancer implements TokenEnhancer {

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        Authentication userAuthentication = authentication.getUserAuthentication();
        if (userAuthentication != null) {
            Object principal = authentication.getUserAuthentication().getPrincipal();
            //把用户标识以userDetails这个Key加入到JWT的额外信息中去
            Map<String, Object> additionalInfo = new HashMap<>();
            additionalInfo.put("userDetails", principal);
            additionalInfo.put("torlesse", "torlesse");
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
        }
        return accessToken;
    }
}

WebSecurityConfig配置

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private HealthUserDetailsService healthUserDetailsService;

    /**
     * 密码的加密方式
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 身份验证管理器
     * 通过自定义实现userDetailsService来实现
     * 配置了使用BCryptPasswordEncoder哈希来保存用户的密码(生产环境的用户密码肯定不能是明文保存)
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //注入userDetailsService的实现类
//        auth.userDetailsService(healthUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
        // 直接加密密码采用直接比较
        auth.userDetailsService(healthUserDetailsService).passwordEncoder(NoOpPasswordEncoder.getInstance());
    }

    /**
     * 设置认证管理器 便于我们使用 ,使用默认的认证管理器即可
     *
     * @return
     * @throws Exception
     */
    @Override
    @Bean(value = "authenticationManager")
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 设置拦截器
     * 除了"/login","/oauth/authorize"请求外,设置为任意的请求都需要登录认证
     *
     * @param http
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .antMatchers("/login", "/oauth/authorize", "/oauth/token")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .csrf().disable();
    }

    /**
     * 静态资源放行【如果存在静态资源的话】
     *
     * @param webSecurity
     */
    @Override
    public void configure(WebSecurity webSecurity) {
        //静态资源放行
        webSecurity.ignoring().antMatchers("/dist/**", "/moudle/**", "/plugins/**");
    }
}

HealthUserDetailsService

@Slf4j
@Component(value = "healthUserDetailsService")
public class HealthUserDetailsService implements UserDetailsService {

    @Autowired
    private UserCheckService userCheckService;

    @Autowired
    private MemberFeignClient memberFeignClient;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("username 账号校验: {}", username);
        // 查询数据库操作
        if (userCheckService.queryUserInfo(username) == null) {
            throw new UsernameNotFoundException("the user is not found");
        } else {
            Result result = memberFeignClient.getUserAndAuthInfo(username);

            HashMap hashMap = JSONObject.parseObject(JSON.toJSONString(result.getData()), HashMap.class);
            String password = String.valueOf(hashMap.get("password"));
            ArrayList<String> roleBos = JSONObject.parseObject(JSON.toJSONString(hashMap.get("roleBos")), ArrayList.class);
            Set<String> permissions = JSONObject.parseObject(JSON.toJSONString(hashMap.get("permissions")), Set.class);
            List<SimpleGrantedAuthority> authorities = new ArrayList<>();
            for (String roleBo : roleBos) {
                authorities.add(new SimpleGrantedAuthority(roleBo));
            }
            for (String permission : permissions) {
                authorities.add(new SimpleGrantedAuthority(permission));
            }
            // 线上环境应该通过用户名查询数据库获取加密后的密码 userBo.getPassword()
            // 返回自定义的 healthUserDetailsService
            User user = new User(username, password, authorities);
            return user;
        }
    }
}

2.2、搭建资源服务器:health-member-service

pom.xml 添加依赖oauth2

	<!--oauth2-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
        <version>2.2.5.RELEASE</version>
    </dependency>

资源服务器配置

@Configuration
@EnableResourceServer//启动资源服务器
@EnableGlobalMethodSecurity(prePostEnabled = true)//启动注解的方式进行权限控制
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Bean
    public BCryptPasswordEncoder getBCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 声明了资源服务器的ID是memberservice,声明了资源服务器的TokenStore是JWT
     *
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("memberservice").tokenStore(tokenStore());
    }

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

    /**
     * 配置公钥
     *
     * @return
     */
    @Bean
    protected JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        Resource resource = new ClassPathResource("public.cert");
        String publicKey = null;
        try {
            publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        converter.setVerifierKey(publicKey);
        return converter;
    }


    /**
     * 配置了除了/register/web路径之外的请求可以匿名访问,其他都需要鉴权
     * anonymous() 允许匿名用户访问
     * permitAll() 无条件允许访问
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/register/web").anonymous()
                .antMatchers(HttpMethod.GET, "/auth/**").anonymous()
                .antMatchers(HttpMethod.POST, "/auth/**").anonymous()
                .antMatchers("/member/**").authenticated()
                .anyRequest().permitAll();

    }
}

application.yml

security:
  oauth2:
    client:
      grantType: password
      clientId: member-service
      clientSecret: 123456
      accessTokenUri: http://localhost:8090/oauth/token
      userAuthorizationUri: http://localhost:8090/oauth/authorize
      scope: TEST
    resource:
      jwt:
        key-value: |
          -----BEGIN PUBLIC KEY-----
          MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwR84LFHwnK5GXErnwkmD
          mPOJl4CSTtYXuOEUYf3ni8GNRJauIuW0rVX9O2gun6wVFKkWYiMoBSjsNMSI3ZI3
          w5JYgh+ldHvA+MIex2QXfOZx920M1fPUiuUPgmnTFS+Z3lmK3/T6jJnmciUPY1pe
          h4MXL6YzeI0q4W12lXHfLbVOL6YzeI0q4W12lXHfLbThKmAUpAWFDwf9/uUA//l
          3PLchmV6VwTcUaaHp5W8Af/GuQnkFwSguOEUYf3ni8GNRO1DikacPhrOQgdxtqk
          LciRTa884uQnkFwSguOEUYf3ni8GNRJauIuW0rVXhMOs78pKvCKmo53M0tqeC6ul
          +QIDAQAB
          -----END PUBLIC KEY-----

2.3 自定义授权模式

Oauth2.0具有多种授权许可机制协议:授权码许可机制、客户端凭据机制、资源拥有者凭据机制(密码模式)和隐式许可机制。

在源码中即可看到四种模式的实现类,还有一个RefreshTokenGranter则是刷新令牌,用于access_token失效时刷新过期时间。
在这里插入图片描述

假如现在我需要实现手机验证码登录或者微信扫码登录等功能的时候,我们该如何处理呢?

我们可以继承AbstractTokenGranter实现自定义授权模式。

手机短信验证码模式如下:

继承AbstractTokenGranter类, 实现手机验证码自定义模式

@Slf4j
public class SmsCodeGranter extends AbstractTokenGranter {

    private static final String GRANT_TYPE = "sms_code";

    protected final AuthenticationManager authenticationManager;

    protected final UserMapper userMapper;

    public SmsCodeGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
                          ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, UserMapper userMapper) {
        super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        this.authenticationManager = authenticationManager;
        this.userMapper = userMapper;
    }

    @Override
    protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {

        Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters());
        String telephone = parameters.get("telePhone");
        String code = parameters.get("code");

        if (StringUtils.isEmpty(telephone) || StringUtils.isEmpty(code)) {
            throw new InvalidGrantException("参数错误.");
        }
        CheckParam checkParam = new CheckParam();
        checkParam.setTelePhone(telephone);
        User user = userMapper.selectUserByCondition(checkParam.getTelePhone());

        log.info("telephone = {}, code = {}, user = {}", telephone, code, JSON.toJSONString(user));
        // 根据手机号码查询用户信息
        if (user == null) {
            throw new InvalidGrantException("手机号码填写错误.");
        }

        Authentication userAuth = new TelePhoneAuthenticationToken(user.getUsername(), user.getPassword(), telephone, code);
        ((AbstractAuthenticationToken) userAuth).setDetails(parameters);

        try {
            userAuth = this.authenticationManager.authenticate(userAuth);
        } catch (AccountStatusException var8) {
            throw new InvalidGrantException("当前用户已经被锁定,请联系客服.");
        } catch (BadCredentialsException var9) {
            throw new InvalidGrantException("用户信息查询异常,请确认是否注册.");
        } catch (InternalAuthenticationServiceException var10) {
            throw new InvalidGrantException("验证码校验失败.");
        }

        if (userAuth != null && userAuth.isAuthenticated()) {
            OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest);
            return new OAuth2Authentication(storedOAuth2Request, userAuth);
        } else {
            throw new InvalidGrantException("Could not authenticate user: " + telephone);
        }

    }
}

手机验证码token

public class TelePhoneAuthenticationToken extends UsernamePasswordAuthenticationToken {

    private String telePhone;

    private String code;

    /**
     * @param principal 用户名
     */
    public TelePhoneAuthenticationToken(Object principal, Object credentials, String telePhone, String code) {
        super(principal, credentials);
        setAuthenticated(false);
        this.telePhone = telePhone;
        this.code = code;
    }

    public String getTelePhone() {
        return telePhone;
    }

    public String getCode() {
        return code;
    }
}

具体案例请参照Gitee项目:案例项目代码

授权服务器
在这里插入图片描述
资源服务器
在这里插入图片描述
自定义授权模式
在这里插入图片描述

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-13 23:35:31  更:2022-04-14 00:02:47 
 
开发: 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/24 22:06:22-

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