finaldata2.0oem(finaldata2.0序列号)

理论

finaldata2.0oem(finaldata2.0序列号)

OAuth是一个关于授权的开放网络标准,用于授权第三方应用获取用户数据。它是目前最流行的授权机制,目前的版本是2.0。

应用场景

如果你正在“网站A”上冲浪,看到一个你非常喜欢的帖子,当你忍不住喜欢它的时候,它会提示你登录。

打开登录页面,你会发现除了最简单的账号密码登录,还为我们提供了微博、微信、QQ等快捷的登录方式。假设选择了快速登录,会提示我们扫码或者输入账号密码登录。

成功登录后,QQ/微信的昵称和头像会回填到“网站A”中,然后就可以点赞了。

名词定义

在详细解释oauth2之前,我们先来看看其中用到的名词的定义:

Client:客户端,它本身不会存储用户快捷登录的账号和密码,只是通过资源拥有者的授权去请求资源服务器的资源,即例子中的网站A;Resource Owner:资源拥有者,通常是用户,即例子中拥有QQ/微信账号的用户;Authorization Server:认证服务器,可以提供身份认证和用户授权的服务器,即给客户端颁发token和校验token;Resource Server:资源服务器,存储用户资源的服务器,即例子中的QQ/微信存储的用户信息;认证流程

如oauth2官网认证流程图所示,我们来分析一下:

A客户端向资源拥有者发送授权申请;B资源拥有者同意客户端的授权,返回授权码;C客户端使用授权码向认证服务器申请令牌token;D认证服务器对客户端进行身份校验,认证通过后发放令牌;E客户端拿着认证服务器颁发的令牌去资源服务器请求资源;F资源服务器校验令牌的有效性,返回给客户端资源信息;

为了更好的理解,阿q特意画了一张图:

至此,相信大家对理论知识已经掌握的差不多了。接下来,我们进入实战训练。

实战

在正式开始构建项目之前,让我们做一些准备工作:要使用oauth2的服务,我们必须先创建几个表。

数据库

oauth2相关的建表语句可以参考官方初始化sql,也可以查看AQ项目中的init.sql文件,私信回复“oauth2”获取源代码。

至于表的结构,你可以大致了解一下字段的含义,在init.sql文件中Q已经解释过了。

oauth_client_details:存储客户端的配置信息,操作该表的类主要是JdbcClientDetailsService.java;oauth_access_token:存储生成的令牌信息,操作该表的类主要是JdbcTokenStore.java;oauth_client_token:在客户端系统中存储从服务端获取的令牌数据,操作该表的类主要是JdbcClientDetailsService.java;oauth_code:存储授权码信息与认证信息,即只有grant_type为authorization_code时,该表才会有数据,操作该表的类主要是JdbcAuthorizationCodeServices.java;oauth_approvals:存储用户的授权信息;oauth_refresh_token:存储刷新令牌的refresh_token,如果客户端的grant_type不支持refresh_token,那么不会用到这张表,操作该表的类主要是JdbcTokenStore;

在oauth_client_details表中添加一条数据。

Client_id:cheetah_one //客户端的名称,必须唯一。resource_ids:product_api //客户机可以访问的资源id的集合。用逗号(,)分隔client _ secret:$ 2a $ 10 $ h/tmlpvxozjjhxdyjen 22 ensjgaciomfpoc 9 js 9 oonwiddanrqeoi//客户端作用域的访问密码:read,write //客户端应用的权限范围。可选值包括读取、写入和信任。如果有多个权限范围,用逗号(,)分隔,authorized _ grant _ types:client _ credentials,implicit,authorization _ code,refresh _ token,password//指定客户端支持的grant _ type,可选值包括authorization _ code,password,refresh _ token,implicit,client _ credentials,逗号(,)分隔web _ server _ redirect _ URI:客户端的http://www.baidu.com//The重定向URI,可以是空。当grant_type为authorization_code或implicit时,在Oauth的过程中,会使用并检查注册时填写的redirect_uri是否与access_token_validity一致:43200 //设置客户端的access_token的有效时间值(单位:秒),可选。如果未设置任何值,则使用默认的有效时间值(60 * 60 * 12,12小时)autoApproval:false //设置用户。假& # 39;,可选值包括& # 39;真& # 39;,'假& # 39;, '阅读& # 39;,'写& # 39;密码是在数据库中加密的,可以在这个路径下自己生成。

与用户相关的表也在init.sql文件中,表结构非常简单,可以自己查阅。我的初始化数据是

依赖引入

& lt依赖性& gt& ltgroupId & gtorg . spring framework . boot & lt;/groupId & gt;& ltartifactId & gtspring-boot-starter-web & lt;/artifact id & gt;& lt/dependency & gt;& lt依赖性& gt& ltgroupId & gtorg . spring framework . cloud & lt;/groupId & gt;& ltartifactId & gtspring-cloud-starter-security & lt/artifact id & gt;& lt/dependency & gt;& lt依赖性& gt& ltgroupId & gtorg . spring framework . cloud & lt;/groupId & gt;& ltartifactId & gtspring-cloud-starter-oauth 2 & lt;/artifact id & gt;& lt/dependency & gt;& lt依赖性& gt& ltgroupId & gtorg . spring framework . security & lt/groupId & gt;& ltartifactId & gtspring-security-jwt & lt;/artifact id & gt;& lt/dependency & gt;至于其他依赖项,可以根据需要介绍,不赘述,回复“oauth2”获取源代码。

资源服务

配置文件配置服务端口、应用程序名称、数据库、mybatis和日志。

编写一个简单的控制层代码来模拟资源访问。

@ rest controller @ request mapping(& # 34;/product & # 34;)公共类product controller { @ get mapping(& # 34;/find all & # 34;)公共字符串find all(){ return & # 34;产品列表查询成功& # 34;;}}然后创建一个配置类来继承ResourceServerConfigureAdapter并添加@EnableResourceServer批注来启动资源服务并重写两个configure方法。

/* * *指定token的持久化策略* InMemoryTokenStore表示在内存中存储token * Redis TokenStore表示在Redis中存储token * JdbcTokenStore表示在数据库中存储token * @ return */@ bean public TokenStore JdbcTokenStore(){ return new JdbcTokenStore(data source);}/* * *指定当前资源的id和令牌的存储策略* @ param resources * @ Throws Exception */@ override public void configure(resources serversecurityconfigurer resources)抛出异常{//这里的id可以写入配置文件。在这里,我们将编写resources . resourceid(& # 34;产品_ api & # 34).token store(jdbcTokenStore());}/* * *设置请求权限和头处理* @ param http * @ throws exception */@ override public void configure(http security http)throws exception {//固定格式http.authorizeRequests() //指定不同请求方法访问资源所需的权限。一般的查询是read,剩下的是write . ant matchers(http method . get,& # 34;/**").访问(& # 34;# oauth 2 . has scope(& # 39;阅读& # 39;)") .antMatchers(HttpMethod。帖子,& # 34;/**").访问(& # 34;# oauth 2 . has scope(& # 39;写& # 39;)") .antMatchers(HttpMethod。补丁,& # 34;/**").访问(& # 34;# oauth 2 . has scope(& # 39;写& # 39;)") .antMatchers(HttpMethod。放,& # 34;/**").访问(& # 34;# oauth 2 . has scope(& # 39;写& # 39;)") .antMatchers(HttpMethod。删除,& # 34;/**").访问(& # 34;# oauth 2 . has scope(& # 39;写& # 39;)") .和()。标题()。addHeaderWriter((请求,响应)-& gt;{//如果域名不同或者子域名不同,而且是ajax请求,就会出现跨域问题。//允许跨域response . add header(& # 34;访问控制允许来源& # 34;,"*");//跨域会有预检请求,如果失败了,真正的请求就发不出去了。//如果是跨域的预检请求,请求头信息会原封不动的传下来,否则预检请求会丢失请求头信息(主要是令牌信息)if (request.getmethod()。等于(& # 34;选项& # 34;)){ response . set header(& # 34;访问控制允许方法& # 34;,request . get header(& # 34;访问控制允许方法& # 34;));response . set header(& # 34;访问控制允许标题& # 34;,request . get header(& # 34;访问控制允许标题& # 34;));} });}当然我们也可以配置忽略检查的url,在上面的public void configure(http security http)throws exception中配置。

ExpressionUrlAuthorizationConfigurer & lt;HttpSecurity & gt。ExpressionInterceptUrlRegistry config = http . request matchers()。anyRequest()。和()。authorizeRequests();properties.getUrls()。forEach(e-& gt;{ config.antMatchers(e)。permit all();});因为需要检查,所以我注释掉了相应的代码。可以回复“oauth2”下载源代码,自己查看。

然后将带有UserDetails的SysUser和带有GrantedAuthority的SysRole放入项目中。请求发出后,oauth2会帮我们自己验证。

认证服务

配置文件配置服务端口、应用程序名称、数据库、mybatis和日志。

安全配置

它类似于之前配置的安全+JWT组合拳。如果不知道,可以先看看这篇文章。

①用继承UserDetailsService的ISysUserService的实现类SysUserServiceImpl重写loadUserByUsername方法。

@ override public user details loaduserbysusername(String username)抛出UsernameNotFoundException { return this . base mapper . select one(new LambdaQueryWrapper & lt;SysUser & gt().eq(SysUser::getUsername,username));} ②继承WebSecurityConfigurerAdapter类,添加@EnableWebSecurity批注,重写方法。

/* * *指定身份验证对象的源和加密方法* @ param auth * @ throws Exception */@ override public void configure(AuthenticationManager Builder auth)抛出异常{ auth . userdailsservice(userService)。password encoder(password encoder());}/* * *安全拦截机制(最重要)* @ param HTTP Security * @ throws exception */@ override public void configure(HTTP Security HTTP Security)throws exception { HTTP Security//CSRF被禁用是因为没有使用session.csrf()。禁用()。authorize Requests()//登录接口和静态资源不需要认证。蚂蚁匹配器(& # 34;/log in * & # 34;,"/CSS/* & # 34;).permitAll() //除上述请求之外的所有请求都需要经过身份验证才能访问。anyRequest()。authenticated() //返回HttpSecurity进行进一步定制,证明是一个新配置的开始。和()。formLogin() //如果不指定此页面,将跳转到默认页面//。log in page(& #)/log in . html & # 34;) .loginProcessingUrl(& # 34;/log in & # 34;).permanent()//身份验证失败处理类。failure handler(customauthenticationfailurehandler);}/** * AuthenticationManager对象要在OAuth2.0认证服务中使用,事先把它放入IOC容器* @ return * @ throws exception */@ override @ bean公共认证管理器认证管理器bean()throws exception { return super . authentic ation manager bean();}授权服务器配置

①继承authorizationserverconfiguratadapter类,添加@EnableAuthorizationServer注释启动认证服务。

②依赖注入,注入7个实例Bean对象

/* * *数据库连接池对象*/private final data source data source;/* * *身份验证业务对象*/private final isysuserserviceUserservice;/* * *授权码模式的特殊对象*/私有最终身份验证管理器身份验证管理器;/* * *客户端信息源* @ return */@ bean公共jdbcclientdetailservice jdbcclientdetailservice(){ return new jdbcclientdetailservice(data source);}/** *令牌保存策略* @ return */@ bean公共令牌存储令牌存储(){ return new JDBC Token Store(data source);}/* * *授权信息保存策略* @ return */@ bean公共批准存储approval store(){ return new JDBC approval store(data source);}/* * *授权码模式数据源* @ return */@ bean公共授权码服务授权码服务(){ return new JDBC authorization code services(数据源);} ③覆盖方法进行配置。

/* * *用于配置ClientDetailsService) *客户端详细信息在此初始化*指定客户端信息的数据库来源* @ param clients * @ throws Exception */@ override public void configure(ClientDetailsServiceConfigurer clients)抛出异常{ clients . with client details(jdbcClientDetailsService());}/* * *检测令牌的策略* @ param security * @ Throws Exception */@ override public void configure(授权服务器安全配置器安全)抛出异常{security //允许客户端将令牌以表单形式传递给我们。AllowFormAuthenticationForClients()//验证令牌需要身份验证。checkTokenAccess(& # 34;is authenticated()& # 34;);}/* * * OAuth 2.0的主要配置信息* @ param endpoints * @ Throws Exception */@ override public void configure(authorization server endpointsconfigurer endpoints)抛出异常{endpoints //刷新令牌时,会验证当前用户是否通过认证。用户详细信息服务(userservice)。Approvalstore (approvalstore())。authenticationManager(认证管理器)。authorizationCodeServices(authorizationCodeServices())。token store(token store());}关于用户表和权限表的其他代码,请参考源代码,回复“oauth2”获取源代码。

模式授权码模式

我们前面讲的是基于授权码模式,这种模式被称为最安全的模式。获取令牌的操作在两台服务器中进行,大大降低了令牌泄露的风险。

启动两个服务,当我们再次请求127.0.0.1:9002/product/findAll接口时,会提示如下错误

{ "错误& # 34;: "未经授权& # 34;, "错误描述& # 34;: "访问此资源需要完全身份验证& # 34;} ①调用接口获取授权码。

发送127.0.0.1:9001/oauth/authorize?response _ type = code & client _ id = cheetah _ one请求,前面的路径是固定的形式。response _ type = code表示获得授权码,client _ id = cheetah _ one表示客户端的名称是我们数据库配置的数据。

此页面是oauth2的默认页面。输入用户的帐户密码并单击登录将提示我们进行授权。这是在数据库的oauth_client_details表中将autoapprove设置为false的效果。

选择批准,点击授权按钮,你会发现我们设置的回调地址(oauth_client_details表中的web_server_redirect_uri)和code值拼接在一起,就是授权码。

查看数据库,发现数据中已经存储了oauth_approvals和oauth_code表。

获取授权码以获得令牌

获得令牌后,数据将存储在oauth_access_token和oauth_refresh_token表中,以供以后进行身份验证。但是oauth_code表中的数据是被清除的,因为代码值是直接暴露在网页链接上的,oauth2为了防止别人获取非法的代码请求,专门设置为只使用一次。

用获取的令牌请求资源服务的接口。这个时候,有两种方式可以请求。

接下来,我们来看看oauth2的其他模式。

简化模式

所谓简化模式就是授权码模式的简化,省略了授权码模式中获取授权码的步骤,直接请求获取令牌。

流程:发送请求127.0.0.1:9001/oauth/authorize?response_type=token & client _ id = cheetah _ one跳转到登录页面登录,response _ type = token表示获取令牌。

输入账号密码登录后,令牌会直接在浏览器中返回,所以我们可以像授权码一样携带令牌请求资源。

这种模式的缺点是token直接暴露在浏览器中,非常不安全,不推荐。

密码模式

在密码模式下,用户需要向客户端提供账号和密码才能向认证服务器申请令牌,所以这种模式要求用户对客户端的信任度很高。

流程:请求如下

成功获取后,您可以访问资源。

客户端模式

客户端模式不再属于oauth2。用户直接在客户端注册,然后客户端去认证服务器获取令牌时不需要携带用户信息。与用户完全分离,所以不存在授权问题。

按如下方式发送请求

成功获取后,您可以访问资源。

刷新token

权限校验

除了在数据库中为客户端配置资源服务,我们还可以动态地为用户分配接口的权限。

①打开安全性内置的动态配置。

打开资源服务时,向ResourceServerConfig类添加注释@ enableGlobalMethodSecurity(secure enabled = true,PreposteEnabled = true)。

②给界面添加权限。

@ get mapping(& # 34;/find all & # 34;)@ Secured(& # 34;角色_产品& # 34;)公共字符串find all(){ return & # 34;产品列表查询成功& # 34;;} ③用户登录时设置用户权限。

@ override public user details loaduserbysusername(String username)throws UsernameNotFoundException { sys user sys user = this . base mapper . select one(new LambdaQueryWrapper & lt;SysUser & gt().eq(SysUser::getUsername,username));sysuser . setrolelist(authority utils . commaseparatedstringtoauthoritylist(& # 34;角色_产品& # 34;));返回sysUser}然后测试会发现可以正常访问。

采坑包名问题

当我创建项目时,我为产品和服务器模块设置了不同的包名,导致在发送资源请求时出错。

经过分析得知,用户的信息在登录账号时会存储在oauth_access_token表的authentication中,在令牌验证时会根据token_id取出这个字段进行反序列化。如果此时发现包名不一致,令牌解析会失败,因此资源请求会失败。

解决方案想法

两个项目的包名改为一致;可以将用户和权限的实体抽成单独的模块,供其它模块引用;loadUserByUsername方法中使用的用户实体类不需要继承UserDetailsService类,每次返回时用user类包装一下即可;数据库问题

我在做权限验证测试的时候,发现设置权限的时候少了一个字,导致请求一直出错。修改后继续请求,仍然提示权限不足。

于是我清除了数据库中oauth_refresh_token和oauth_access_token的数据,重新开始测试。

个人认为令牌是在生成令牌时在数据库中找到的,所以不刷新令牌。但是在检查的时候,我用带权限标识的token来检查,导致失败。

至于其他小坑,这里就不赘述了。如果遇到什么问题,建议大家按照流程仔细查看我的源代码,回复“oauth2”获取源代码。

小结

本文从原理、应用场景、认证流程入手,对oauth2进行了基本的讲解,带大家一起携手完成项目。大家在测试授权码模式、简化模式、密码模式、客户端模式的时候,要重点关注授权码模式。

原文:https://mp.weixin.qq.com/s/BNA42Agb_RFFiXJ8mlfhqw

作者:阿q说代码

如果你觉得这篇文章对你有帮助,请喜欢并关注它。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。

作者:美站资讯,如若转载,请注明出处:https://www.meizw.com/n/300836.html

发表回复

登录后才能评论