备案 控制台
开发者社区 开发与运维 文章 正文

OAuth2.0授权码模式原理与实战

简介: OAuth2.0授权码模式原理与实战

OAuth2.0是目前比较流行的一种开源授权协议,可以用来授权第三方应用,允许在不将用户名和密码提供给第三方应用的情况下获取一定的用户资源,目前很多网站或APP基于微信或QQ的第三方登录方式都是基于OAuth2实现的。本文将基于OAuth2中的授权码模式,采用数据库配置方式,搭建认证服务器与资源服务器,完成授权与资源的访问。

流程分析

在OAuth2中,定义了4种不同的授权模式,其中授权码模式(authorization code)功能流程相对更加完善,也被更多的系统采用。首先使用图解的方式简单了解一下它的授权流程:

image.png

对上面的流程进行一下说明:
1、用户访问客户端
2、客户端将用户导向认证服务器
3、用户登录,并对第三方客户端进行授权
4、认证服务器将用户导向客户端事先指定的重定向地址,并附上一个授权码
5、客户端使用授权码,向认证服务器换取令牌
6、认证服务器对客户端进行认证以后,发放令牌
7、客户端使用令牌,向资源服务器申请获取资源
8、资源服务器确认令牌,向客户端开放资源

在对授权码模式的流程有了一定基础的情况下,我们开始动手搭建项目。

image.png

1、在Project中创建两个module,采用认证服务器和资源服务器分离的架构:

image.png

2、spring-security-oauth2是对Oauth2协议规范的一种实现,这里可以直接使用spring-cloud-starter-oauth2,就不需要分别引入spring-securityoauth2了。在父pom中引入:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

3、数据库建表,OAuth2需要的表结构如下:

DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token`  (
  `token_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `token` blob NULL,
  `authentication_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `client_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authentication` blob NULL,
  `refresh_token` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (
  `client_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `resource_ids` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `client_secret` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `scope` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authorized_grant_types` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `web_server_redirect_uri` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authorities` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `access_token_validity` int(11) NULL DEFAULT NULL,
  `refresh_token_validity` int(11) NULL DEFAULT NULL,
  `additional_information` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
  `autoapprove` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT 'false',
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code`  (
  `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authentication` blob NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token`  (
  `token_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `token` blob NULL,
  `authentication` blob NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

oauth_access_token:存储生成的access_token,由类JdbcTokenStore操作

oauth_client_details:存储客户端的配置信息,由类JdbcClientDetailsService操作

oauth_code:存储服务端系统生成的code的值,由类JdbcAuthorizationCodeServices操作

oauth_refresh_token:存储刷新令牌的refresh_token,如果客户端的grant_type不支持refresh_token,那么不会用到这张表,同样由类JdbcTokenStore操作

其余spring security相关的用户表、角色表以及权限表的表结构在这里省略,可以在文末的Git地址中下载。

image.png

认证服务器是服务提供者专门用来处理认证授权的服务器,主要负责获取用户授权并颁发token,以及完成后续的token认证工作。认证部分功能主要由spring security 负责,授权则由oauth2负责。

1、开启Spring Security配置

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

通过@Configuration@EnableWebSecurity 开启Spring Security配置,继承WebSecurityConfigurerAdapter的方法,实现个性化配置。如果使用内存配置用户,可以重写其中的configure方法进行配置,由于我们使用数据库中的用户信息,所以不需要在这里进行配置。并且采用认证服务器和资源服务器分离,也不需要在这里对服务资源进行权限的配置。

在类中创建了两个Bean,分别是用于处理认证请求的认证管理器AuthenticationManager,以及配置全局统一使用的密码加密方式BCryptPasswordEncoder,它们会在认证服务中被使用。

2、开启并配置认证服务器

@Configuration
@EnableAuthorizationServer 
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;    //认证管理器
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;//密码加密方式
    @Autowired
    private DataSource dataSource;  // 注入数据源
    @Autowired
    private UserDetailsService userDetailsService; //自定义用户身份认证
    @Bean
    public ClientDetailsService jdbcClientDetailsService(){
        //将client信息存储在数据库中
        return new JdbcClientDetailsService(dataSource);
    }
    @Bean
    public TokenStore tokenStore(){
        //对token进行持久化存储在数据库中,数据存储在oauth_access_token和oauth_refresh_token
        return new JdbcTokenStore(dataSource);
    }
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        //加入对授权码模式的支持
        return new JdbcAuthorizationCodeServices(dataSource);
    }
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //设置客户端的配置从数据库中读取,存储在oauth_client_details表
        clients.withClientDetails(jdbcClientDetailsService());
    }
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokenStore())//token存储方式
                .authenticationManager(authenticationManager)// 开启密码验证,来源于 WebSecurityConfigurerAdapter
                .userDetailsService(userDetailsService)// 读取验证用户的信息
                .authorizationCodeServices(authorizationCodeServices())
                .setClientDetailsService(jdbcClientDetailsService());
    }
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //  配置Endpoint,允许请求,不被Spring-security拦截
        security.tokenKeyAccess("permitAll()") // 开启/oauth/token_key 验证端口无权限访问
                .checkTokenAccess("isAuthenticated()") // 开启/oauth/check_token 验证端口认证权限访问
                .allowFormAuthenticationForClients()// 允许表单认证
                .passwordEncoder(passwordEncoder);   // 配置BCrypt加密
    }
}

在类中,通过@EnableAuthorizationServer 注解开启认证服务,通过继承父类AuthorizationServerConfigurerAdapter,对以下信息进行了配置:

ClientDetailsServiceConfigurer:配置客户端服务,这里我们通过JdbcClientDetailsService从数据库读取相应的客户端配置信息,进入源码可以看到客户端信息是从表oauth_client_details中拉取。

AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点,以及令牌服务的配置信息。该类作为一个装载类,装载了Endpoints所有的相关配置。

AuthorizationServerSecurityConfigurer:配置令牌端点(endpoint)的安全约束,OAuth2开放了端点用于检查令牌,/oauth/check_token/oauth/token_key这些端点默认受到保护,在这里配置可被外部调用。

3、采用从数据库中获取用户信息的方式进行身份验证

@Service
public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    private TbUserService userService;
    @Autowired
    private TbPermissionService permissionService;
    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        TbUser tbUser = userService.getUserByUserName(userName);
        if (tbUser==null){
            throw new UsernameNotFoundException("username : "+userName+" is not exist");
        }
        List<GrantedAuthority> authorities=new ArrayList<>();
        //获取用户权限
        List<TbPermission> permissions = permissionService.getByUserId(tbUser.getId());
        permissions.forEach(permission->{
            authorities.add(new SimpleGrantedAuthority(permission.getEname()));
        });
        return new User(tbUser.getUsername(),tbUser.getPassword(),authorities);
    }
}

创建UserDetailServiceImpl 实现UserDetailsService接口,并实现loadUserByUsername方法,根据用户名从数据库查询用户信息及权限。

4、启动服务

首先发起请求获取授权码(code),直接访问下面的url

http://localhost:9004/oauth/authorize?client_id=client1&redirect_uri=http://localhost:8848/nacos&response_type=code&scope=select

看一下各个参数的意义:

client_id:因为认证服务器要知道是哪一个应用在请求授权,所以client_id就是认证服务器给每个应用分配的id

redirect_uri:重定向地址,会在这个重定向地址后面附加授权码,让第三方应用获取code

response_typecode表明采用授权码认证模式

scope:需要获得哪些授权,这个参数的值是由服务提供商定义的,不能随意填写

首先会重定向到登录验证页面,因为之前的url中只明确了第三方应用的身份,这里要确定第三方应用要请求哪一个用户的授权。输入数据库表tb_user中配置的用户信息 admin/123456

image.png

注意url中请求的参数必须和在数据库中的表oauth_client中配置的相同,如果不存在或信息不一致都会报错,在参数填写错误时会产生如下报错信息:

image.png

如果参数完全匹配,会请求用户向请求资源的客户端client授权:

image.png

点击Authorize同意授权,会跳转到redirect_uri定义的重定向地址,并在url后面附上授权码code

image.png

这样,用户的登录和授权的操作都在浏览器中完成了,接下来我们需要获取令牌,发送post请求到/oauth/token接口,使用授权码获取access_token。在发送请求时,需要在请求头中包含clientIdclientSecret,并且携带参数 grant_typecoderedirect_uri,这里会对redirect_uri做二次验证:

image.png

这样,就通过/oauth/token端点获取到了access_token,并一同拿到了它的令牌类型、过期时间、授权范围信息,这个令牌将在请求资源服务器的资源时被使用。由于这个令牌在一定时间内有效,客户端可以在有效期内,将令牌保存在本地,避免重复申请。

image.png

资源服务器简单来说就是资源的访问入口,主要负责处理用户数据的api调用,资源服务器中存储了用户数据,并对外提供http服务,可以将用户数据返回给经过身份验证的客户端。资源服务器和认证服务器可以部署在一起,也可以分离部署,我们这里采用分开部署的形式。

1、配置资源服务器

@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Bean
    @Primary
    public RemoteTokenServices remoteTokenServices(){
        final RemoteTokenServices tokenServices=new RemoteTokenServices();
        //设置授权服务器check_token Endpoint 完整地址
        tokenServices.setCheckTokenEndpointUrl("http://localhost:9004/oauth/check_token");
        //设置客户端id与secret,注意:client_secret 值不能使用passwordEncoder加密
        tokenServices.setClientId("client1");
        tokenServices.setClientSecret("client-secret");
        return tokenServices;
    }
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        http.authorizeRequests()
                .anyRequest().authenticated();
    }
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("oauth2").stateless(true);
    }
}

在类中主要实现了以下功能:

@EnableResourceServer注解表明开启OAuth2资源服务器,在请求资源服务器的请求前,需要通过认证服务器获取access_token令牌,然后在访问资源服务器中的资源时需要携带令牌才能正常进行请求

通过RemoteTokenServices实现自定义认证服务器,这里配置了我们之前创建的认证服务器

重写configure(HttpSecurity http)方法,开启所有请求需要授权才可以访问

配置资源相关设置configure(ResourceServerSecurityConfigurer resources),这里只设置resourceId,作为该服务资源的唯一标识

2、测试接口,负责提供用户信息

@RestController
public class TestController {
    @GetMapping("/user/{name}")
    public User user(@PathVariable String name){
        return new User(name, 20);
    }
}

3、启动服务

不携带access_token,直接访问接口 http://127.0.0.1:9005/user/hydra:

image.png

使用Postman,在Authorization中配置使用Bearer Token,并填入从认证服务器获取的access_token(或在Headers中的Authorization字段直接填写Bearer 'access_token')。

image.png

再次访问接口,可以正常访问接口资源,这样就实现了对资源服务器内资源的访问,完成了认证服务器与资源服务器的整合。

项目git地址: https://github.com/trunks2008/oauth2

码农参上
目录
相关文章
Pseudocode
|
30天前
|
安全 Java 数据安全/隐私保护
Spring Security OAuth 认证流程浅析:授权码模式
【1月更文挑战第16天】上一篇[Spring Security OAuth 认证流程浅析:密码模式],简单分析了密码模式授权流程的源码,这篇来试着分析 OAuth 中最具代表性的授权码模式。
Pseudocode
96 4
宁在春
|
30天前
|
存储 应用服务中间件 API
Oauth 2 授权码模式
Oauth 2 授权码模式
宁在春
87 1
得过且过的勇者y
|
30天前
|
存储 缓存 前端开发
自定义OAuth2组件实现对授权码登录模式的封装
所谓OAuth2其实就是Open Authorization,即开放授权,是一种授权机制或者说是一种协议。OAuth2允许用户授权第三方应用访问其存储在开放平台(授权服务器)中的数据而不需要提供密码。授权服务器根据OAuth2协议标准制订一套授权的API,第三方网站接入开放平台之后即可通过其提供的API来实现用户授权和获取授权服务器中用户的信息的功能。
得过且过的勇者y
61 0
码猿技术专栏
|
存储 安全 前端开发
几行代码搞定 Spring Cloud OAuth2 授权码模式3个页面定制
几行代码搞定 Spring Cloud OAuth2 授权码模式3个页面定制
码猿技术专栏
227 0
Heron
|
11月前
|
存储 缓存 安全
SpringSecurity基础-认证和授权概述
RBAC是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。
Heron
69 0
游客ig3oyhuxp5p5q
|
11月前
|
存储 缓存 安全
一.SpringSecurity基础-认证和授权概述
SpringSecurity基础-认证和授权概述
游客ig3oyhuxp5p5q
129 0
码猿技术专栏
|
安全 Java 数据安全/隐私保护
OAuth2.0实战!玩转认证、资源服务异常自定义这些骚操作!
OAuth2.0实战!玩转认证、资源服务异常自定义这些骚操作!
码猿技术专栏
301 0
chengfengpolang
|
API 数据安全/隐私保护
Yii2.0框架中如何进行身份验证和授权操作?支持哪些认证方式和授权方式?
Yii2.0框架中如何进行身份验证和授权操作?支持哪些认证方式和授权方式?
chengfengpolang
146 0
阿里云叶秋
|
存储 NoSQL 数据库
认证授权流程及原理分析
认证授权流程及原理分析
阿里云叶秋
173 0
bqospzg5rfs7g
|
存储 安全 Java
案例之oauth2认证所需资源说明|学习笔记
快速学习案例之oauth2认证所需资源说明
bqospzg5rfs7g
110 0
案例之oauth2认证所需资源说明|学习笔记

热门文章

最新文章

  • 1
    2016云栖社区技术专题&课程大盘点-你想要的都在这里
  • 2
    谷歌浏览器Chrome播放rtsp视频流解决方案
  • 3
    K8S Ingress Controller 发布公告
  • 4
    分库分表:应用场景、方式方法、面临问题
  • 5
    suid/sgid/sticky
  • 6
    一些零碎的思想需要找个地方记录起来
  • 7
    语句for( ;1;)是什么意思?
  • 8
    VC/MFC 使edit控件不能进行粘贴操作
  • 9
    操作系统复习笔记
  • 10
    ASP.NET C# 用后台代码实现 跨域名访问 返回HTML代码
  • 1
    创空间重启后出现log问题
    32
  • 2
    python3提取指定字段
    17
  • 3
    Java基础19-一文搞懂Java集合类框架,以及常见面试题(二)
    28
  • 4
    python3之flask快速入门教程Demo
    20
  • 5
    python3 服务端使用CGI脚本处理POST的Json数据
    18
  • 6
    Java基础19-一文搞懂Java集合类框架,以及常见面试题(一)
    23
  • 7
    Docker 和 vmware 共存工作
    27
  • 8
    Java集合-----HashMap实例
    13
  • 9
    关于hadoop搭建的问题org.apache.hadoop.io.nativeio.NativeIO.java
    14
  • 10
    hadoop-eclipse-plugin(版本hadoop2.7.3)
    23
  • 相关电子书

    更多
  • 安全机制与User账户身份验证实战
  • 低代码开发师(初级)实战教程
  • 阿里巴巴DevOps 最佳实践手册
  • 下一篇
    2024年阿里云免费云服务器及学生云服务器申请教程参考

    深圳SEO优化公司永州设计公司网站南昌seo排名哪家好白城阿里店铺托管推荐烟台优秀网站设计揭阳网站开发哪家好泰安优秀网站设计哪家好台州外贸网站制作哪家好清远seo推荐衡水推广网站多少钱东莞关键词按天扣费价格甘南网站改版公司天津网站设计报价揭阳网站设计模板多少钱亳州SEO按天收费白银百度竞价包年推广推荐枣庄网站搭建报价天津企业网站建设哪家好垦利百度关键词包年推广多少钱潍坊设计网站报价武汉SEO按效果付费报价林芝建设网站通辽阿里店铺运营推荐滨州优秀网站设计多少钱上饶如何制作网站清远百搜标王推荐铁岭推广网站多少钱晋城品牌网站设计推荐铜川模板制作报价临夏网页制作推荐盐田英文网站建设哪家好歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

    深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化