| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> Spring Security 整合OAuth2 -> 正文阅读 |
|
[Java知识库]Spring Security 整合OAuth2 |
OAuth2.0介绍 OAuth(Open Authorization)是一个关于授权(authorization)的开放网络标准,允许用户授权第三方 应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他 们数据的所有内容。OAuth在全世界得到广泛应用,目前的版本是2.0版。 OAuth协议:rfc6749 协议特点:
应用场景
基本概念
OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务提供商"进行交互。 优缺点优点:
缺点:
运行流程OAuth 2.0的运行流程如下图,摘自RFC 6749。 (A)用户打开客户端以后,客户端要求用户给予授权。 (B)用户同意给予客户端授权。 (C)客户端使用上一步获得的授权,向认证服务器申请令牌。 (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。 (E)客户端使用令牌,向资源服务器申请获取资源。 (F)资源服务器确认令牌无误,同意向客户端开放资源。 授权模式客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0一共分成四种授权类型(authorization grant)
授权码模式和密码模式比较常用。 第三方应用申请令牌之前,都必须先到系统备案,说明自己的身份,然后会拿到两个身份识别码:客户端 ID(client ID)和客户端密钥(client secret)。这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。 授权码模式 授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。 这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。 适用场景:目前主流的第三方验证都是采用这种模式 主要流程: (A)用户访问客户端,后者将前者导向授权服务器。 (B)用户选择是否给予客户端授权。 (C)用户给予授权,授权服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。 (D)客户端收到授权码,附上早先的"重定向URI",向授权服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。 (E)授权服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。 快速使用 引入依赖 <dependency> ? <groupId>org.springframework.cloud</groupId> ? <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> ? ? ? ? ? ? ? ? ? 配置授权服务器 注意:实际项目中clinet_id 和client_secret 是配置在数据库中,省略spring security相关配置,可以参考 [Spring Security 入门教程] ?https://zhuanlan.zhihu.com/p/429570570? @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { ? ? ?@Autowired ? ?private PasswordEncoder passwordEncoder; ? ? ?@Autowired ? ?private TokenStore tokenStore; ? ? ?@Override ? ?public void configure(ClientDetailsServiceConfigurer clients) throws Exception { ? ? ? ? ?clients.inMemory() ? ? ? ? ? ? ? ?//配置client_id ? ? ? ? ? ? ? .withClient("client") ? ? ? ? ? ? ? ?//配置client_secret ? ? ? ? ? ? ? .secret(passwordEncoder.encode("123123")) ? ? ? ? ? ? ? ?//配置访问token的有效期 ? ? ? ? ? ? ? .accessTokenValiditySeconds(3600) ? ? ? ? ? ? ? ?//配置刷新token的有效期 ? ? ? ? ? ? ? .refreshTokenValiditySeconds(864000) ? ? ? ? ? ? ? ?//配置redirect_uri,用于授权成功后跳转 ? ? ? ? ? ? ? .redirectUris("http://www.baidu.com") ? ? ? ? ? ? ? ?//配置申请的权限范围 ? ? ? ? ? ? ? .scopes("all") ? ? ? ? ? ? ? ?//配置grant_type,表示授权类型 ? ? ? ? ? ? ? .authorizedGrantTypes("authorization_code","implicit"); ? ? ? } } 配置资源服务器 @Configuration @EnableResourceServer public class ResourceServiceConfig extends ResourceServerConfigurerAdapter { ? ? ?@Override ? ?public void configure(HttpSecurity http) throws Exception { ? ? ? ? ?http.authorizeRequests() ? ? ? ? ? ? ? .anyRequest().authenticated() ? ? ? ? ? ? ? .and().requestMatchers().antMatchers("/user/**"); ? ? ? ? ? } ? } 1、A网站(客户端)提供一个链接,用户点击后就会跳转到 B (授权服务器)网站,授权用户数据给 A 网站使用。 https://b.com/oauth/authorize? ? response_type=code& ? ? ? ? ? ?# 表示授权类型,必选项,此处的值固定为"code" client_id=CLIENT_ID& ? ? ? ? ? # 表示客户端的ID,必选项 redirect_uri=CALLBACK_URL& ? ? # redirect_uri:表示重定向URI,可选项 scope=read ? ?# 表示申请的权限范围,可选项 read只读 ? 2、用户跳转后,B 网站会要求用户登录,然后询问是否同意给予 A 网站授权。用户表示同意,这时 B 网站就会跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码 https://a.com/callback?code=AUTHORIZATION_CODE ? #code参数就是授权码 ? ? ? ? ? ? ? ? ? 3、A 网站拿到授权码以后,就可以在后端,向 B 网站请求令牌。 用户不可见,服务端行为 https://b.com/oauth/token? client_id=CLIENT_ID& client_secret=CLIENT_SECRET& ? ? # client_id和client_secret用来让 B 确认 A 的身份,client_secret参数是保密的,因此只能在后端发请求 grant_type=authorization_code& ? # 采用的授权方式是授权码 code=AUTHORIZATION_CODE& ? ? ? ? # 上一步拿到的授权码 redirect_uri=CALLBACK_URL # 令牌颁发后的回调网址 ? ? ? ? 4、B 网站收到请求以后,就会颁发令牌。具体做法是向redirect_uri指定的网址,返回数据。 { ? ? ? "access_token":"ACCESS_TOKEN", ? ? # 令牌 ? "token_type":"bearer", ? "expires_in":2592000, ? "refresh_token":"REFRESH_TOKEN", ? "scope":"read", ? "uid":100101, ? "info":{...} } 密码模式 如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为"密码式"(password)。 在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而授权服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。 适用场景:公司搭建的授权服务器 (A)用户向客户端提供用户名和密码。 (B)客户端将用户名和密码发给授权服务器,向后者请求令牌。 (C)授权服务器确认无误后,向客户端提供访问令牌。 配置 spring security @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ? ? ? ?@Bean ? ?public PasswordEncoder passwordEncoder(){ ? ? ? ?return new BCryptPasswordEncoder(); ? } ? ? ?@Autowired ? ?private UserService userService; ? ? ? ?@Override ? ?protected void configure(AuthenticationManagerBuilder auth) throws Exception { ? ? ? ?// 获取用户信息 ? ? ? ?auth.userDetailsService(userService); ? } ? ? ? ?@Override ? ?protected void configure(HttpSecurity http) throws Exception { ? ? ? ?http.formLogin().permitAll() ? ? ? ? ? ? ? .and().authorizeRequests() ? ? ? ? ? ? ? .antMatchers("/oauth/**").permitAll() ? ? ? ? ? ? ? .anyRequest() ? ? ? ? ? ? ? .authenticated() ? ? ? ? ? ? ? .and().logout().permitAll() ? ? ? ? ? ? ? .and().csrf().disable(); ? } ? ? ? ? ?@Bean ? ?@Override ? ?public AuthenticationManager authenticationManagerBean() throws Exception { ? ? ? ?// oauth2 密码模式需要拿到这个bean ? ? ? ?return super.authenticationManagerBean(); ? } } ? ? @Service public class UserService implements UserDetailsService { ? ? ?@Autowired ? ?@Lazy ? ?private PasswordEncoder passwordEncoder; ? ? ?@Override ? ?public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { ? ? ? ?String password = passwordEncoder.encode("123456"); ? ? ? ?return new User("jack",password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); ? } } ? @Configuration public class RedisConfig { ? ?@Autowired ? ?private RedisConnectionFactory redisConnectionFactory; ? ?@Bean ? ?public TokenStore tokenStore(){ ? ? ? ?return new RedisTokenStore(redisConnectionFactory); ? } } 配置授权服务器 @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig2 extends AuthorizationServerConfigurerAdapter { ? ? @Autowired ? private PasswordEncoder passwordEncoder; ? ? @Autowired ? private AuthenticationManager authenticationManagerBean; ? ? @Autowired ? private UserService userService; ? ? @Autowired ? private TokenStore tokenStore; ? ? ? @Override ? public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { ? ? ? endpoints.authenticationManager(authenticationManagerBean) //使用密码模式需要配置 ? ? ? ? ? ? ? .tokenStore(tokenStore) //指定token存储到redis ? ? ? ? ? ? ? .reuseRefreshTokens(false) //refresh_token是否重复使用 ? ? ? ? ? ? ? .userDetailsService(userService) //刷新令牌授权包含对用户信息的检查 ? ? ? ? ? ? ? .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); //支持GET,POST请求 ? } ? ? @Override ? public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { ? ? ? //允许表单认证 ? ? ? security.allowFormAuthenticationForClients(); ? } ? ? @Override ? public void configure(ClientDetailsServiceConfigurer clients) throws Exception { ? ? ? ? clients.inMemory() ? ? ? ? ? ? ? //配置client_id ? ? ? ? ? ? ? .withClient("client") ? ? ? ? ? ? ? //配置client-secret ? ? ? ? ? ? ? .secret(passwordEncoder.encode("123123")) ? ? ? ? ? ? ? //配置访问token的有效期 ? ? ? ? ? ? ? .accessTokenValiditySeconds(3600) ? ? ? ? ? ? ? //配置刷新token的有效期 ? ? ? ? ? ? ? .refreshTokenValiditySeconds(864000) ? ? ? ? ? ? ? //配置redirect_uri,用于授权成功后跳转 ? ? ? ? ? ? ? .redirectUris("http://www.baidu.com") ? ? ? ? ? ? ? //配置申请的权限范围 ? ? ? ? ? ? ? .scopes("all") ? ? ? ? ? ? ? /** ? ? ? ? ? ? ? ? * 配置grant_type,表示授权类型 ? ? ? ? ? ? ? ? * authorization_code: 授权码模式 ? ? ? ? ? ? ? ? * implicit: 简化模式 ? ? ? ? ? ? ? ? * password: 密码模式 ? ? ? ? ? ? ? ? * client_credentials: 客户端模式 ? ? ? ? ? ? ? ? * refresh_token: 更新令牌 ? ? ? ? ? ? ? ? */ ? ? ? ? ? ? ? .authorizedGrantTypes("authorization_code","implicit","password","client_credentials","refresh_token"); ? } ? ? } 获取token: https://oauth.b.com/token? grant_type=password& ? ? ? # 授权方式是"密码式" username=USERNAME& password=PASSWORD& client_id=CLIENT_ID& client_secret=123123& scope=all ? 客户端模式 客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行 授权。 适用于没有前端的命令行应用,即在命令行下请求令牌。一般用来提供给我们完全信任的服务器端服务。 它的步骤如下: (A)客户端向授权服务器进行身份认证,并要求一个访问令牌。 (B)授权服务器确认无误后,向客户端提供访问令牌。 A 应用在命令行向 B 发出请求。 https://oauth.b.com/token? grant_type=client_credentials& client_id=CLIENT_ID& client_secret=CLIENT_SECRET ? ? ? ? ? ? ? 简化(隐式)模式 有些 Web 应用是纯前端应用,没有后端,必须将令牌储存在前端。RFC 6749 就规定了第二种方式,允许直接向前端颁发令牌,这种方式没有授权码这个中间步骤,所以称为(授权码)"隐藏式"(implicit) 简化模式不通过第三方应用程序的服务器,直接在浏览器中向授权服务器申请令牌,所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。 这种方式把令牌直接传给前端,是很不安全的。因此,只能用于一些安全要求不高的场景,并且令牌的有效期必须非常短,通常就是会话期间(session)有效,浏览器关掉,令牌就失效了。 它的步骤如下: (A)客户端将用户导向授权服务器。 (B)用户决定是否给于客户端授权。 (C)假设用户给予授权,授权服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。 (D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。 (E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。 (F)浏览器执行上一步获得的脚本,提取出令牌。 (G)浏览器将令牌发给客户端。 A 网站提供一个链接,要求用户跳转到 B 网站,授权用户数据给 A 网站使用。 https://b.com/oauth/authorize? response_type=token& ? ? ? ? # response_type参数为token,表示要求直接返回令牌 client_id=CLIENT_ID& redirect_uri=CALLBACK_URL& scope=read ? ? 用户跳转到 B 网站,登录后同意给予 A 网站授权。这时,B 网站就会跳回redirect_uri参数指定的跳转网址,并且把令牌作为 URL 参数,传给 A 网站。 https://a.com/callback#token=ACCESS_TOKEN ? ? #token参数就是令牌,A 网站直接在前端拿到令牌。 ? ? ? ? ? ? ? 令牌的使用A 网站拿到令牌以后,就可以向 B 网站的 API 请求数据了。 此时,每个发到 API 的请求,都必须带有令牌。具体做法是在请求的头信息,加上一个Authorization字段,令牌就放在这个字段里面。 也可以添加请求参数access_token请求数据 localhost/getUser?access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxx |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
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 0:59:13- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |