免费数据查询网站中国建设教育协会的官方网站

当前位置: 首页 > news >正文

免费数据查询网站,中国建设教育协会的官方网站,官网设计多少钱,装修网站合作这里主讲OAuth2.0 学习OAuth2前提#xff1a; 掌握Spring Security Spring Security学习 一 OAuth2.0介绍 OAuth#xff08;Open Authorization#xff09;是一个关于授权#xff08;authorization#xff09;的开放网络标准#xff0c;允许用户授权第三方应用访问他们…这里主讲OAuth2.0 学习OAuth2前提 掌握Spring Security Spring Security学习 一 OAuth2.0介绍 OAuthOpen Authorization是一个关于授权authorization的开放网络标准允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容。OAuth在全世界得到广泛应用目前的版本是2.0版。 OAuth协议https://tools.ietf.org/html/rfc6749 协议特点 简单不管是OAuth服务提供者还是应用开发者都很易于理解与使用安全没有涉及到用户密钥等信息更安全更灵活开放任何服务提供商都可以实现OAuth任何软件开发商都可以使用OAuth 1.1 应用场景 原生app授权app登录请求后台接口为了安全认证所有请求都带token信息然后登录验证、请求后台数据。前后端分离单页面应用前后端分离框架前端请求后台数据需要进行oauth2安全认证比如使用vue、react或者h5开发的app第三方应用授权登录比如QQ微博微信的授权登录。 有一个云冲印的网站可以将用户储存在Google的照片冲印出来。用户为了使用该服务必须让云冲印读取自己储存在Google上的照片。只有得到用户的授权Google才会同意云冲印读取这些照片。那么云冲印怎样获得用户的授权呢 传统方法是用户将自己的Google用户名和密码告诉云冲印后者就可以读取用户的照片了。这样的做法有以下几个严重的缺点 云冲印为了后续的服务会保存用户的密码这样很不安全。Google不得不部署密码登录而我们知道单纯的密码登录并不安全。云冲印拥有了获取用户储存在Google所有资料的权力用户没法限制云冲印获得授权的范围和有效期。用户只有修改密码才能收回赋予云冲印的权力。但是这样做会使得其他所有获得用户授权的第三方应用程序全部失效。只要有一个第三方应用程序被破解就会导致用户密码泄漏以及所有被密码保护的数据泄漏。
生活中常见的oauth2场景京东商城https://www.jd.com/接入微信开放平台可以通过微信登录。 登录流程分析 https://www.processon.com/view/link/60a32e7a079129157118740f 1.2 基本概念 1Third-party application第三方应用程序又称客户端client即例子中的云冲印。 2HTTP serviceHTTP服务提供商简称服务提供商即例子中的Google。 3Resource Owner资源所有者又称用户user。 4User Agent用户代理比如浏览器。 5Authorization server授权服务器即服务提供商专门用来处理认证授权的服务器。 6Resource server资源服务器即服务提供商存放用户生成的资源的服务器。它与授权服务器可以是同一台服务器也可以是不同的服务器。 OAuth的作用就是让客户端安全可控地获取用户的授权与服务提供商进行交互。 1.3 优缺点 优点 更安全客户端不接触用户密码服务器端更易集中保护广泛传播并被持续采用短寿命和封装的token资源服务器和授权服务器解耦集中式授权简化客户端HTTP/JSON友好易于请求和传递token考虑多种客户端架构场景客户可以具有不同的信任级别 缺点 协议框架太宽泛造成各种实现的兼容性和互操作性差不是一个认证协议本身并不能告诉你任何用户信息。 二 OAuth2的设计思路 OAuth在客户端与服务提供商之间设置了一个授权层authorization layer。“客户端不能直接登录服务提供商”只能登录授权层以此将用户与客户端区分开来。客户端登录授权层所用的令牌token与用户的密码不同。用户可以在登录的时候指定授权层令牌的权限范围和有效期客户端登录授权层以后服务提供商根据令牌的权限范围和有效期向客户端开放用户储存的资料。 OAuth 2.0的运行流程如下图摘自RFC 6749 A用户打开客户端以后客户端要求用户给予授权。 B用户同意给予客户端授权。 C客户端使用上一步获得的授权向授权服务器申请令牌。 D授权服务器对客户端进行认证以后确认无误同意发放令牌。 E客户端使用令牌向资源服务器申请获取资源。 F资源服务器确认令牌无误同意向客户端开放资源。 令牌token与密码password的作用是一样的都可以进入系统但是有三点差异。 1令牌是短期的到期会自动失效用户自己无法修改。密码一般长期有效用户不修改就不会发生变化。 2令牌可以被数据所有者撤销会立即失效。密码一般不允许被他人撤销。 3令牌有权限范围scope。对于网络服务来说只读令牌就比读写令牌更安全。密码一般是完整权限。 上面这些设计保证了令牌既可以让第三方应用获得权限同时又随时可控不会危及系统安全。这就是 OAuth 2.0 的优点。 2.1 客户端授权模式 客户端必须得到用户的授权authorization grant才能获得令牌access token。OAuth 2.0 对于如何颁发令牌的细节规定得非常详细。具体来说一共分成四种授权类型authorization grant即四种颁发令牌的方式适用于不同的互联网场景。 授权码模式authorization code密码模式resource owner password credentials简化(隐式)模式implicit客户端模式client credentials 不管哪一种授权方式第三方应用申请令牌之前都必须先到系统备案说明自己的身份然后会拿到两个身份识别码客户端 IDclient ID和客户端密钥client secret。这是为了防止令牌被滥用没有备案过的第三方应用是不会拿到令牌的。 授权码模式 授权码authorization code方式指的是第三方应用先申请一个授权码然后再用该码获取令牌。 这种方式是最常用的流程安全性也最高它适用于那些有后端的 Web 应用。授权码通过前端传送令牌则是储存在后端而且所有与资源服务器的通信都在后端完成。这样的前后端分离可以避免令牌泄漏。 适用场景目前市面上主流的第三方验证都是采用这种模式
它的步骤如下 A用户访问客户端后者将前者导向授权服务器。 B用户选择是否给予客户端授权。 C假设用户给予授权授权服务器将用户导向客户端事先指定的重定向URIredirection URI同时附上一个授权码。 D客户端收到授权码附上早先的重定向URI向授权服务器申请令牌。这一步是在客户端的后台的服务器上完成的对用户不可见。 E授权服务器核对了授权码和重定向URI确认无误后向客户端发送访问令牌access token和更新令牌refresh token。 A网站提供一个链接用户点击后就会跳转到 B 网站授权用户数据给 A 网站使用。下面就是 A 网站跳转 B 网站的一个示意链接。 https://b.com/oauth/authorize? response_typecode #要求返回授权码code client_idCLIENT_ID #让 B 知道是谁在请求 redirect_uriCALLBACK_URL #B 接受或拒绝请求后的跳转网址 scoperead # 要求的授权范围这里是只读 客户端申请授权的URI包含以下参数 response_type表示授权类型必选项此处的值固定为codeclient_id表示客户端的ID必选项redirect_uri表示重定向URI可选项scope表示申请的权限范围可选项state表示客户端的当前状态可以指定任意值授权服务器会原封不动地返回这个值。 用户跳转后B 网站会要求用户登录然后询问是否同意给予 A 网站授权。用户表示同意这时 B 网站就会跳回redirect_uri参数指定的网址。跳转时会传回一个授权码就像下面这样。 https://a.com/callback?codeAUTHORIZATION_CODE #code参数就是授权码 A 网站拿到授权码以后就可以在后端向 B 网站请求令牌。 用户不可见服务端行为 https://b.com/oauth/token? client_idCLIENT_ID client_secretCLIENT_SECRET # client_id和client_secret用来让 B 确认 A 的身份,client_secret参数是保密的因此只能在后端发请求 grant_typeauthorization_code # 采用的授权方式是授权码 codeAUTHORIZATION_CODE # 上一步拿到的授权码 redirect_uriCALLBACK_URL # 令牌颁发后的回调网址 B 网站收到请求以后就会颁发令牌。具体做法是向redirect_uri指定的网址发送一段 JSON 数据。 { access_token:ACCESS_TOKEN, # 令牌token_type:bearer,expires_in:2592000,refresh_token:REFRESH_TOKEN,scope:read,uid:100101,info:{…}}简化(隐式)模式 有些 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_typetoken # response_type参数为token表示要求直接返回令牌 client_idCLIENT_ID redirect_uriCALLBACK_URL scoperead 用户跳转到 B 网站登录后同意给予 A 网站授权。这时B 网站就会跳回redirect_uri参数指定的跳转网址并且把令牌作为 URL 参数传给 A 网站。 https://a.com/callback#tokenACCESS_TOKEN #token参数就是令牌A 网站直接在前端拿到令牌。 密码模式 如果你高度信任某个应用RFC 6749 也允许用户把用户名和密码直接告诉该应用。该应用就使用你的密码申请令牌这种方式称为密码式password。 在这种模式中用户必须把自己的密码给客户端但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下比如客户端是操作系统的一部分或者由一个著名公司出品。而授权服务器只有在其他授权模式无法执行的情况下才能考虑使用这种模式。 适用场景自家公司搭建的授权服务器 它的步骤如下 A用户向客户端提供用户名和密码。 B客户端将用户名和密码发给授权服务器向后者请求令牌。 C授权服务器确认无误后向客户端提供访问令牌。 A 网站要求用户提供 B 网站的用户名和密码拿到以后A 就直接向 B 请求令牌。整个过程中客户端不得保存用户的密码。 https://oauth.b.com/token? grant_typepassword # 授权方式是密码式 usernameUSERNAME passwordPASSWORD client_idCLIENT_ID B 网站验证身份通过后直接给出令牌。注意这时不需要跳转而是把令牌放在 JSON 数据里面作为 HTTP 回应A 因此拿到令牌。 客户端模式 客户端模式Client Credentials Grant指客户端以自己的名义而不是以用户的名义向服务提供商进行授权。 适用于没有前端的命令行应用即在命令行下请求令牌。一般用来提供给我们完全信任的服务器端服务。
它的步骤如下 A客户端向授权服务器进行身份认证并要求一个访问令牌。 B授权服务器确认无误后向客户端提供访问令牌。 A 应用在命令行向 B 发出请求。 https://oauth.b.com/token? grant_typeclient_credentials client_idCLIENT_ID client_secretCLIENT_SECRET B 网站验证通过以后直接返回令牌。
2.2 令牌的使用 A 网站拿到令牌以后就可以向 B 网站的 API 请求数据了。 此时每个发到 API 的请求都必须带有令牌。具体做法是在请求的头信息加上一个Authorization字段令牌就放在这个字段里面。 curl -H “Authorization: Bearer ACCESS_TOKEN” “https://api.b.com” 也可以通过添加请求参数access_token请求数据。
2.3 更新令牌 令牌的有效期到了如果让用户重新走一遍上面的流程再申请一个新的令牌很可能体验不好而且也没有必要。OAuth 2.0 允许用户自动更新令牌。 具体方法是B 网站颁发令牌的时候一次性颁发两个令牌一个用于获取数据另一个用于获取新的令牌refresh token 字段。令牌到期前用户使用 refresh token 发一个请求去更新令牌。 https://b.com/oauth/token? grant_typerefresh_token # grant_type参数为refresh_token表示要求更新令牌 client_idCLIENT_ID client_secretCLIENT_SECRET refresh_tokenREFRESH_TOKEN # 用于更新令牌的令牌 三 Spring Security OAuth2快速开始 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。Spring Security 主要实现了Authentication认证解决who are you? 和 Access Control访问控制也就是what are you allowed to do也称为Authorization。Spring Security在架构上将认证与授权分离并提供了扩展点。 认证Authentication 用户认证就是判断一个用户的身份是否合法的过程用户去访问系统资源时系统要求验证用户的身份信息身份合法方可继续访问不合法则拒绝访问。常见的用户身份认证方式有用户名密码登录二维码登录手机短信登录指纹认证等方式。 授权Authorization 授权是用户认证通过根据用户的权限来控制用户访问资源的过程拥有资源的访问权限则正常访问没有权限则拒绝访问。 将OAuth2和Spring Security集成就可以得到一套完整的安全解决方案。我们可以通过Spring Security OAuth2构建一个授权服务器来验证用户身份以提供access_token并使用这个access_token来从资源服务器请求数据。 3.1 授权服务器 Authorize Endpoint 授权端点进行授权Token Endpoint 令牌端点经过授权拿到对应的TokenIntrospection Endpoint 校验端点校验Token的合法性Revocation Endpoint 撤销端点撤销授权 3.2 整体架构 流程 用户访问,此时没有Token。Oauth2RestTemplate会报错这个报错信息会被Oauth2ClientContextFilter捕获并重定向到授权服务器。授权服务器通过Authorization Endpoint进行授权并通过AuthorizationServerTokenServices生成授权码并返回给客户端。客户端拿到授权码去授权服务器通过Token Endpoint调用AuthorizationServerTokenServices生成Token并返回给客户端客户端拿到Token去资源服务器访问资源一般会通过Oauth2AuthenticationManager调用ResourceServerTokenServices进行校验。校验通过可以获取资源。 3.3 授权码模式 引入依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId /dependencydependencygroupIdorg.springframework.security.oauth/groupIdartifactIdspring-security-oauth2/artifactIdversion2.3.4.RELEASE/version /dependency或者 引入spring cloud oauth2依赖 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-oauth2/artifactId /dependency!– spring cloud – dependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversionHoxton.SR8/versiontypepom/typescopeimport/scope/dependency/dependencies /dependencyManagement配置 spring security Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter {Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().permitAll().and().authorizeRequests().antMatchers(/oauth/).permitAll().anyRequest().authenticated().and().logout().permitAll().and().csrf().disable();} }Service public class UserService implements UserDetailsService {Autowiredprivate PasswordEncoder passwordEncoder;Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {String password passwordEncoder.encode(123456);return new User(fox,password, AuthorityUtils.commaSeparatedStringToAuthorityList(admin));} }RestController RequestMapping(/user) public class UserController {RequestMapping(/getCurrentUser)public Object getCurrentUser(Authentication authentication) {return authentication.getPrincipal();} }配置授权服务器 Configuration EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {Autowiredprivate PasswordEncoder passwordEncoder;Overridepublic 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);} }配置资源服务器 Configuration EnableResourceServer public class ResourceServiceConfig extends ResourceServerConfigurerAdapter {Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().requestMatchers().antMatchers(/user/);} }测试 获取授权码 http://localhost:8080/oauth/authorize?response_typecodeclient_idclient 或者 http://localhost:8080/oauth/authorize?response_typecodeclient_idclientredirect_urihttp://www.baidu.comscopeall 登录之后进入 获取令牌 根据授权码通过post请求获取 或者 或者
3.4 简化模式 authorizedGrantType添加implicit 测试 http://localhost:8080/oauth/authorize?client_idclientresponse_typetokenscopeallredirect_urihttp://www.baidu.com 登录之后进入授权页面确定授权后浏览器会重定向到指定路径并以Hash的形式存放在重定向uri的fargment中
3.5 密码模式 修改WebSecurityConfig增加AuthenticationManager Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter {Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().permitAll().and().authorizeRequests().antMatchers(/oauth/).permitAll().anyRequest().authenticated().and().logout().permitAll().and().csrf().disable();}BeanOverridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();} }修改AuthorizationServerConfig配置 Configuration EnableAuthorizationServer public class AuthorizationServerConfig2 extends AuthorizationServerConfigurerAdapter {Autowiredprivate PasswordEncoder passwordEncoder;Autowiredprivate AuthenticationManager authenticationManagerBean;Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.authenticationManager(authenticationManagerBean) //使用密码模式需要配置.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); //支持GET,POST请求}Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {//允许表单认证security.allowFormAuthenticationForClients();}Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {/*授权码模式*http://localhost:8080/oauth/authorize?response_typecodeclient_idclientredirect_urihttp://www.baidu.comscopeall*http://localhost:8080/oauth/authorize?response_typecodeclient_idclient** password模式* http://localhost:8080/oauth/token?usernamefoxpassword123456grant_typepasswordclient_idclientclient_secret123123scopeall** 客户端模式* http://localhost:8080/oauth/token?grant_typeclient_credentialsscopeallclient_idclientclient_secret123123*/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: 授权码* password 密码* client_credentials: 客户端*/.authorizedGrantTypes(authorization_code,password,client_credentials);} }获取令牌 通过浏览器测试需要配置支持get请求和表单验证 http://localhost:8080/oauth/token?usernamefoxpassword123456grant_typepasswordclient_idclientclient_secret123123scopeall 通过Postman测试 访问资源
3.6 客户端模式 获取令牌
3.7 更新令牌 使用oauth2时如果令牌失效了可以使用刷新令牌通过refresh_token的授权模式再次获取access_token。只需修改认证服务器的配置添加refresh_token的授权模式即可。 修改授权服务器配置增加refresh_token配置 Autowired private UserService userService; 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(ClientDetailsServiceConfigurer clients) throws Exception {/*授权码模式*http://localhost:8080/oauth/authorize?response_typecodeclient_idclientredirect_urihttp://www.baidu.comscopeall*http://localhost:8080/oauth/authorize?response_typecodeclient_idclient password模式* http://localhost:8080/oauth/token?usernamefoxpassword123456grant_typepasswordclient_idclientclient_secret123123scopeall** 客户端模式* http://localhost:8080/oauth/token?grant_typeclient_credentialsscopeallclient_idclientclient_secret123123*/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: 授权码* password 密码* client_credentials: 客户端* refresh_token: 更新令牌*/.authorizedGrantTypes(authorization_code,password,client_credentials,refresh_token); }通过密码模式测试 http://localhost:8080/oauth/token?grant_typerefresh_tokenclient_idclientclient_secret123123refresh_tokendc03bdc2-ca3b-4690-9265-d31a21896d02 3.8 基于redis存储Token 引入依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId /dependency dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactId /dependency修改application.yml spring:redis:host: 127.0.0.1database: 0编写redis配置类 Configuration public class RedisConfig {Autowiredprivate RedisConnectionFactory redisConnectionFactory;Beanpublic TokenStore tokenStore(){return new RedisTokenStore(redisConnectionFactory);} }在授权服务器配置中指定令牌的存储策略为Redis 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请求 }