oauth2授权码模式单点登录

news/2024/10/5 0:21:03 标签: java, 微服务, 安全

文章目录

  • 前言
  • 一、单点登录是什么?
  • 二、oauth2授权码模式单点登录流程
    • 1.流程图
    • 2. 代码相关
    • 2. 验证流程
  • 总结


前言

oauth2 有四种模式,常用的为密码和授权码,剩下两种几乎不用

  1. 密码模式,很好理解,就是根据输入的用户名/密码进行登录认证的,最终返回一个合法token
  2. 授权码(grant_type = authorization_code), 是利用唯一的客户端信息,申请的一个临时授权码,然后根据授权码换取合法token,可以利用这个特性,达到单点登录的效果

一、单点登录是什么?

简而言之: 登录一次,可以访问所有当前网站被信任的其他网站,无需再次登录;
例如:

我登录了淘宝,然后再次访问里面的其他应用的时候,不会再次登录,而是直接就进去了(天猫 聚划算 咸鱼)

二、oauth2授权码模式单点登录流程

1.流程图

授权码登录流程

操作步骤说明:

  1. 用户访问服务A,需要使用服务提供商的数据(用户信息),首次访问没有任何信息,需要登录;
  2. 服务A通过重定向跳转到服务提供商的统一登录页面。在重定向中构建授权码请求,授权许可
  3. 用户选择是否给予客户端授权访问服务提供商(用户信息)数据的权限
  4. 输入用户信息,用户给予授权。权限系统通过重定向(redirect_uri)并携带 授权凭证(code)跳转客户端。
  5. 服务A提供redirect_uri的接口,接受授权码code,将授权凭证(code)发送给鉴权中心服务器,服务A服务器携带授权码(code)、客户端id(client_id)和秘钥(client_secret)向认证服务器请求访问令牌(access_token)
  6. 鉴权中心认证服务器核对授权码信息,确认无误后,向客户端发送访问令牌(access_token)和更新令牌(refresh_token)
  7. 客户端持有访问令牌(access_token)和需要请求的参数向服务A发起资源请求,服务A拿着token去鉴权中心校验,无误后将资源返回给客户端
  8. 客户端访问受信任的服务B,发现服务B未授权,携带服务B的url去鉴权中心
  9. 去鉴权中心鉴权,发现该用户已经登录,token有效,重定向到服务B的redirect_uri,直接签发已有token,授权资源
    10.客户端携带token 访问服务B

2. 代码相关

pom

  • 鉴权中心
<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--
 注意:spring-cloud-starter-oauth2中包含spring-cloud-starter-security和spring-security-oauth2-autoconfigure
 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
  • 服务A

与上面一直

配置

  • 鉴权中心
    标记为鉴权服务中心
java">
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    // 令牌端点的安全约束
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                // 允许表单登录
                .allowFormAuthenticationForClients()
                // 公开token
                .tokenKeyAccess("permitAll()")
                // 全部允许验证token
                .checkTokenAccess("permitAll()");
    }
    

    // 用内存存储
    // 自动创建UserDetailsServiceInfo实例
    @Autowired
    private UserDetailsServiceInfo userDetailsServiceInfo;
    // 自动加载WebSecurityConfig中的authenticationManagerBean()方法的返回值AuthenticationManager对象
    @Autowired
    private AuthenticationManager authenticationManager;
    // 令牌端点配置
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        // 认证管理器,密码模式时使用
        endpoints.authenticationManager(this.authenticationManager)
                // 会自动调用UserDetailsServiceInfo下的loadUserByUsername()方法
                .userDetailsService(this.userDetailsServiceInfo);
    }
    
    // 自动加载WebSecurityConfig中的bcryptPasswordEncoder()方法的返回值BCryptPasswordEncoder对象
    @Autowired
    private PasswordEncoder bcryptPasswordEncoder;
    // 客户端信息配置
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                // 客户端名称
                .withClient("web")
                // 客户端密钥
                .secret(this.bcryptPasswordEncoder.encode("123456"))
                // 设置授权模式为password
                .authorizedGrantTypes("password", "refresh_token")
                .scopes("all")
                // 设置token有效期
                .accessTokenValiditySeconds(920)
                // 设置刷新token的有效期
                .refreshTokenValiditySeconds(920)
                .autoApprove(true)

                .and()

                // 客户端名称
                .withClient("app")
                // 客户端密钥
                .secret(this.bcryptPasswordEncoder.encode("123456"))
                // 设置授权模式为password
                .authorizedGrantTypes("password", "authorization_code", "refresh_token")
                .scopes("all")
                // 设置token有效期
                .accessTokenValiditySeconds(920)
                // 设置刷新token的有效期
                .refreshTokenValiditySeconds(920)
                // 配置授权码模式必须配置uri,否则授权后跳转无权限
                .redirectUris("http://127.0.0.1:8082/data/common")
                .autoApprove(true);

    }

}

security,web启用

java">@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    // 用户权限管理器,进行用户认证,配置用户签名服务和用户权限控制
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    // 将BCryptPasswordEncoder对象注入Spring容器中,
    // SpringSecurity会使用PasswordEncoder自动密码校验
    @Bean
    public PasswordEncoder bcryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    // 用户授权,配置拦截请求、请求验证、异常处理
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭csrf
        http.csrf().disable();

        // 解决跨域
        http.cors();

        // 开启Spring Security默认的表单登录
        http.formLogin();
                // 根据需求,自定义登录页面,注意不要拦截此Action
//              .loginPage("/login");

        // 设置认证的action
        http.authorizeRequests()
                // 不拦截以下action
                .antMatchers("/sso/register").permitAll()

                // 处了上面的action,都需要鉴权认证
                .anyRequest().authenticated();
    }


}

自定义认证过程

java">@Service
public class UserDetailsServiceInfo implements UserDetailsService {

    // 自动加载WebSecurityConfig中的bcryptPasswordEncoder()方法的返回值BCryptPasswordEncoder对象
    @Autowired
    private PasswordEncoder bcryptPasswordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // (1)根据username查询数据库,找到账号和密码,下面类似查数据库
        if (!"admin".equals(username)){
            // 查不到数据返回null即可
            return null;
        }

        // (2) 对查询的密码进行加密,如果数据库的密码已经加密,此处不做。
        String password = this.bcryptPasswordEncoder.encode("123456");

        // (3) 生成User对象

        /*
        // 使用userdetails自带的UserDetails的对象User
        User user = new User("admin",password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin, secretary"));
        return user;
        */

        // 使用自定义的UserDetails对象UserDetailsInfo
        UserDetailsInfo userDetailsInfo = new UserDetailsInfo("1","admin", password, null);
        return userDetailsInfo;

    }

}
  • 服务A
    标记为资源服务提供者
java">@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {

        //关闭csrf
        http.csrf().disable();

        // 解决跨域
        http.cors();

        // 登录,此处可以不设置,默认会跳转到SpringSecurity的登录页面
//        http.formLogin();

        // 设置认证的action
        http.authorizeRequests()
                // 不拦截以下action
                .antMatchers("/data/common")
                .permitAll()

                // 处了上面的action,都需要鉴权认证
                .anyRequest().authenticated();

    }


}

yml

server:
  port: 8082

security:
  oauth2:
    client:
      # 配置授权服务器参数
      client-id: web
      client-secret: 123456
      # 配置获取token
      access-token-uri: http://127.0.0.1:8080/oauth/token
      # 配置授权码模式认证,如果只有密码模式,此处可以不配置
      # user-authorization-uri: http://127.0.0.1:8080/oauth/authorize

    resource:
      # 验证Token,并返回客户端信息
      token-info-uri: http://127.0.0.1:8080/oauth/check_token

2. 验证流程

最终能获取到一个合法token,并且成功访问到资源

  1. 获取code
  2. 根据code获取token
  3. 携带token访问资源,成功

总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。


http://www.niftyadmin.cn/n/5690492.html

相关文章

快停止这种使用U盘的行为!

前言 现在各行各业的小伙伴基本上都需要用电脑来办公了&#xff0c;你敢说你不需要用电脑办公&#xff1f; 啊哈哈哈&#xff0c;用iPad或者手机办公的也算。 有些小伙伴可能经常996&#xff0c;甚至有时候都是007。有时候到了下班时间&#xff0c;工作还没做完&#xff0c;…

Windows系统编程(二)进程与线程一

进程与线程 进程&#xff1a;直观的讲就是任务管理器中我们看到的东西。 与内核对象句柄相似的&#xff0c;进程也有进程对象句柄&#xff0c;可以进行进程的各种操作如打开关闭。 每个进程都是独立的&#xff0c;在进程启动以后系统分配彼此独立的虚拟内存&#xff0c;此时…

MyBatisPlus——学习笔记

MyBatisPlus 一、导入依赖 <!-- MyBatisPlus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version></dependency><!-- MySql --><de…

场景题1-设计redis的key和value的原则

在设计 Redis 的 key 和 value 时&#xff0c;遵循一些最佳实践和设计原则可以确保系统的性能、可扩展性和易维护性。以下是设计 Redis key 和 value 时的常见原则&#xff1a; 1.RedisKey的设计原则 1.1.简短有意义 1&#xff09;Redis 是内存数据库&#xff0c;key 越短&am…

MAE(平均绝对误差)和std(标准差)计算中需要注意的问题

一、MAE&#xff08;平均绝对误差&#xff09; 计算公式&#xff1a; yi​ 是第i个实际值y^​i​ 是第i个预测值 计算方法&#xff1a; MAE就是求实际值与预测值之间的误差&#xff0c;需要给出预测值和原始的实际值 二、std&#xff08;标准差&#xff09; 计算公式&#x…

代码随想录:冗余连接|、||

如果没接触过并查集&#xff0c;可以先做代码随想录&#xff1a;107、寻找存在的路径-CSDN博客 108. 冗余连接 1、条件准备 并查集find函数&#xff0c;join函数 #include <bits/stdc.h>#define rep(i, l, r) for (int i l; i < r; i)using namespace std;#define…

在Linux系统安装Nginx

注意&#xff1a;Nginx端口号是80(云服务器要放行) 我的是基于yum源安装 安装yum源(下面这4步就好了) YUM源 1、将源文件备份 cd /etc/yum.repos.d/ && mkdir backup && mv *repo backup/ 2、下载阿里源文件 curl -o /etc/yum.repos.d/CentOS-Base.repo ht…

自定义一个星期和按月汇总

上周二到本周一为一个星期 *-------------------------------生成星期一-------------------------------------*READ TABLE GTT_EXCEL INTO DATA(GSS_FIRST) INDEX 1.DO .IF ZDDAT2 IS INITIAL.ZDDAT2 GSS_FIRST-ZDDAT.CALL FUNCTION DAY_IN_WEEKEXPORTINGDATUM GSS_FIRST-…