前言 为什么使用spring-authorization-server? 真实原因:原先是因为个人原因,需要研究新版鉴权服务,看到了spring-authorization-server,使用过程中,想着能不能整合新版本cloud,因此此处先以springboot搭建spring-authorization-server,后续再替换为springcloud2021。
官方原因:原先使用spring security oauth,而该项目已经逐渐被淘汰,虽然网上还是有不少该方案,但秉着技术要随时代更新,从而使用spring-authorization-server
spring 团队正式宣布 spring security oauth 停止维护,该项目将不会再进行任何的迭代
项目构建 以springboot搭建spring-authorization-server(即认证与资源服务器) 数据库相关表结构构建 需要创建3张表,sql分别如下
create table `oauth2_authorization` ( `id` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null, `registered_client_id` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null, `principal_name` varchar(200) character set utf8mb4 collate utf8mb4_unicode_ci not null, `authorization_grant_type` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null, `attributes` varchar(4000) character set utf8mb4 collate utf8mb4_unicode_ci null default null, `state` varchar(500) character set utf8mb4 collate utf8mb4_unicode_ci null default null, `authorization_code_value` blob null, `authorization_code_issued_at` timestamp(0) null default null, `authorization_code_expires_at` timestamp(0) null default null, `authorization_code_metadata` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci null default null, `access_token_value` blob null, `access_token_issued_at` timestamp(0) null default null, `access_token_expires_at` timestamp(0) null default null, `access_token_metadata` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci null default null, `access_token_type` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci null default null, `access_token_scopes` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci null default null, `oidc_id_token_value` blob null, `oidc_id_token_issued_at` timestamp(0) null default null, `oidc_id_token_expires_at` timestamp(0) null default null, `oidc_id_token_metadata` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci null default null, `refresh_token_value` blob null, `refresh_token_issued_at` timestamp(0) null default null, `refresh_token_expires_at` timestamp(0) null default null, `refresh_token_metadata` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci null default null, primary key (`id`) using btree) engine = innodb character set = utf8mb4 collate = utf8mb4_unicode_ci row_format = dynamic; create table `oauth2_authorization_consent` ( `registered_client_id` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null, `principal_name` varchar(200) character set utf8mb4 collate utf8mb4_unicode_ci not null, `authorities` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci not null, primary key (`registered_client_id`, `principal_name`) using btree) engine = innodb character set = utf8mb4 collate = utf8mb4_unicode_ci row_format = dynamic; create table `oauth2_registered_client` ( `id` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null, `client_id` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null, `client_id_issued_at` timestamp(0) not null default current_timestamp(0), `client_secret` varchar(200) character set utf8mb4 collate utf8mb4_unicode_ci null default null, `client_secret_expires_at` timestamp(0) null default null, `client_name` varchar(200) character set utf8mb4 collate utf8mb4_unicode_ci not null, `client_authentication_methods` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci not null, `authorization_grant_types` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci not null, `redirect_uris` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci null default null, `scopes` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci not null, `client_settings` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci not null, `token_settings` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci not null, primary key (`id`) using btree) engine = innodb character set = utf8mb4 collate = utf8mb4_unicode_ci row_format = dynamic; 先进行认证服务器相关配置 pom.xml引入依赖
注意!!!spring boot版本需2.6.x以上,是为后面升级成cloud做准备
org.projectlombok lombok 1.18.22 com.xxxx.iov iov-cloud-framework-web 2.0.0-snapshot org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-web 2.6.6 cn.hutool hutool-all 5.8.0 com.alibaba fastjson 1.2.39 org.springframework.boot spring-boot-starter-security org.springframework.security spring-security-oauth2-authorization-server 0.2.3 org.springframework.security spring-security-cas org.springframework.boot spring-boot-starter-thymeleaf com.alibaba druid-spring-boot-starter 1.2.9 mysql mysql-connector-java 8.0.28 com.baomidou mybatis-plus-boot-starter 3.5.1 com.google.guava guava 31.1-jre 创建自定义登录页面 login.html (可不要,使用自带的登录界面)
login page sign in to start your session
invalid username and password.
you have been logged out.
sign in
i forgot my password
register a new membership
创建自定义授权页面 consent.html(可不要,可使用自带的授权页面)
授权页面 用户授权确认
应用 想要访问您的账号
上述应用程序请求以下权限
请审阅以下选项并勾选您同意的权限
您已对上述应用授予以下权限:
同意授权
取消授权
需要您同意并提供访问权限。
如果您不同意,请单击取消授权,将不会为上述应用程序提供任何您的信息。
修改配置文件 application.yml(配置内容可自行简略)
server: port: 9000 spring: application: name: authorization-server thymeleaf: cache: false datasource: url: jdbc//192.168.1.69:3306/test username: root password: root driver-class-name: com.mysql.cj.jdbc.driver security: oauth2: resourceserver: jwt: issuer-uri: http://127.0.0.1:9000 #认证中心端点,作为资源端的配置 application: security: excludeurls: #excludeurls中存放白名单地址 - /favicon.ico # mybatis plus配置mybatis-plus: mapper-locations: classpath:/mapper/*mapper.xml global-config: # 关闭mp3.0自带的banner banner: false db-config: #主键类型 0:数据库id自增, 1:不操作, 2:用户输入id,3:数字型snowflake, 4:全局唯一id uuid, 5:字符串型snowflake; id-type: auto #字段策略 insert-strategy: not_null update-strategy: not_null select-strategy: not_null #驼峰下划线w转换 table-underline: true # 逻辑删除配置 # 逻辑删除全局值(1表示已删除,这也是mybatis plus的默认配置) logic-delete-value: 1 # 逻辑未删除全局值(0表示未删除,这也是mybatis plus的默认配置) logic-not-delete-value: 0 configuration: #驼峰 map-underscore-to-camel-case: true #打开二级缓存 cache-enabled: true # log-impl: org.apache.ibatis.logging.stdout.stdoutimpl #开启sql日志 新增认证服务器配置文件 authorizationserverconfig
@configuration(proxybeanmethods = false)public class authorizationserverconfig { /** * 自定义授权页面 * 使用系统自带的即不用 */ private static final string custom_consent_page_uri = /oauth2/consent; /** * 自定义userdetailsservice */ @autowired private userservice userservice; /** * * 使用默认配置进行form表单登录 * oauth2authorizationserverconfiguration.applydefaultsecurity(http) */ @bean @order(ordered.highest_precedence) public securityfilterchain authorizationserversecurityfilterchain(httpsecurity http) throws exception { oauth2authorizationserverconfigurer authorizationserverconfigurer = new oauth2authorizationserverconfigurer(); authorizationserverconfigurer.authorizationendpoint(authorizationendpoint -> authorizationendpoint.consentpage(custom_consent_page_uri)); requestmatcher endpointsmatcher = authorizationserverconfigurer.getendpointsmatcher(); http .requestmatcher(endpointsmatcher) .userdetailsservice(userservice) .authorizerequests(authorizerequests -> authorizerequests.anyrequest().authenticated()) .csrf(csrf -> csrf.ignoringrequestmatchers(endpointsmatcher)) .apply(authorizationserverconfigurer); return http.formlogin(customizer.withdefaults()).build(); } /** * 注册客户端应用 */ @bean public registeredclientrepository registeredclientrepository(jdbctemplate jdbctemplate) { // save registered client in db as if in-jdbc registeredclient registeredclient = registeredclient.withid(uuid.randomuuid().tostring()) .clientid(zxg) .clientsecret(123) .clientauthenticationmethod(clientauthenticationmethod.client_secret_basic) .authorizationgranttype(authorizationgranttype.authorization_code) .authorizationgranttype(authorizationgranttype.refresh_token) .authorizationgranttype(authorizationgranttype.client_credentials) // 回调地址 .redirecturi(http://www.baidu.com) // scope自定义的客户端范围 .scope(oidcscopes.openid) .scope(message.read) .scope(message.write) // client请求访问时需要授权同意 .clientsettings(clientsettings.builder().requireauthorizationconsent(true).build()) // token配置项信息 .tokensettings(tokensettings.builder() // token有效期100分钟 .accesstokentimetolive(duration.ofminutes(100l)) // 使用默认jwt相关格式 .accesstokenformat(oauth2tokenformat.self_contained) // 开启刷新token .reuserefreshtokens(true) // refreshtoken有效期120分钟 .refreshtokentimetolive(duration.ofminutes(120l)) .idtokensignaturealgorithm(signaturealgorithm.rs256).build() ) .build(); // save registered client in db as if in-memory jdbcregisteredclientrepository registeredclientrepository = new jdbcregisteredclientrepository(jdbctemplate); registeredclientrepository.save(registeredclient); return registeredclientrepository; } /** * 授权服务:管理oauth2授权信息服务 */ @bean public oauth2authorizationservice authorizationservice(jdbctemplate jdbctemplate, registeredclientrepository registeredclientrepository) { return new jdbcoauth2authorizationservice(jdbctemplate, registeredclientrepository); } /** * 授权确认信息处理服务 */ @bean public oauth2authorizationconsentservice authorizationconsentservice(jdbctemplate jdbctemplate, registeredclientrepository registeredclientrepository) { return new jdbcoauth2authorizationconsentservice(jdbctemplate, registeredclientrepository); } /** * 加载jwk资源 * jwt:指的是 json web token,不存在签名的jwt是不安全的,存在签名的jwt是不可窜改的 * jws:指的是签过名的jwt,即拥有签名的jwt * jwk:既然涉及到签名,就涉及到签名算法,对称加密还是非对称加密,那么就需要加密的 密钥或者公私钥对。此处我们将 jwt的密钥或者公私钥对统一称为 json web key,即 jwk。 */ @bean public jwksource jwksource() { rsakey rsakey = jwksutils.generatersa(); jwkset jwkset = new jwkset(rsakey); return (jwkselector, securitycontext) -> jwkselector.select(jwkset); } /** * 配置 oauth2.0 提供者元信息 */ @bean public providersettings providersettings() { return providersettings.builder().issuer(http://127.0.0.1:9000).build(); } } 新增security的配置文件websecurityconfig
@configuration@enablewebsecurity(debug = true) //开启securitypublic class websecurityconfig { @autowired private applicationproperties properties; /** * 设置加密方式 */ @bean public passwordencoder passwordencoder() {// // 将密码加密方式采用委托方式,默认以bcryptpasswordencoder方式进行加密,兼容ldap,md4,md5等方式// return passwordencoderfactories.createdelegatingpasswordencoder(); // 此处我们使用明文方式 不建议这样 return nooppasswordencoder.getinstance(); } /** * 使用websecurity.ignoring()忽略某些url请求,这些请求将被spring security忽略 */ @bean websecuritycustomizer websecuritycustomizer() { return new websecuritycustomizer() { @override public void customize(websecurity web) { // 读取配置文件application.security.excludeurls下的链接进行忽略 web.ignoring().antmatchers(properties.getsecurity().getexcludeurls().toarray(new string[]{})); } }; } /** * 针对http请求,进行拦截过滤 * * cookiecsrftokenrepository进行csrf保护的工作方式: * 1.客户端向服务器发出get请求,例如请求主页 * 2.spring发送 get 请求的响应以及 set-cookie 标头,其中包含安全生成的xsrf令牌 */ @bean public securityfilterchain httpsecurityfilterchain(httpsecurity httpsecurity) throws exception { httpsecurity .authorizerequests(authorizerequests -> authorizerequests.antmatchers(/login).permitall() .anyrequest().authenticated() ) //使用默认登录页面 //.formlogin(withdefaults()) //设置form登录,设置且放开登录页login .formlogin(fromlogin -> fromlogin.loginpage(/login).permitall()) // spring security csrf保护 .csrf(csrftoken -> csrftoken.csrftokenrepository(new cookiecsrftokenrepository())) // //开启认证服务器的资源服务器相关功能,即需校验token// .oauth2resourceserver()// .accessdeniedhandler(new simpleaccessdeniedhandler())// .authenticationentrypoint(new simpleauthenticationentrypoint())// .jwt() ; return httpsecurity.build(); } } 新增读取application配置的类 applicationproperties
/*** 此步主要是获取配置文件中配置的白名单,可自行舍去或自定义实现其他方式**/@data@component@configurationproperties(application)public class applicationproperties { private final security security = new security(); @data public static class security { private oauth2 oauth2; private list excludeurls = new arraylist(); @data public static class oauth2 { private string issuerurl; } }} 新增 jwksutils 类和 keygeneratorutils,这两个类作为jwt对称加密
public final class jwksutils { private jwksutils() { } /** * 生成rsa加密key (即jwk) */ public static rsakey generatersa() { // 生成rsa加密的key keypair keypair = keygeneratorutils.generatersakey(); // 公钥 rsapublickey publickey = (rsapublickey) keypair.getpublic(); // 私钥 rsaprivatekey privatekey = (rsaprivatekey) keypair.getprivate(); // 构建rsa加密key return new rsakey.builder(publickey) .privatekey(privatekey) .keyid(uuid.randomuuid().tostring()) .build(); } /** * 生成ec加密key (即jwk) */ public static eckey generateec() { // 生成ec加密的key keypair keypair = keygeneratorutils.generateeckey(); // 公钥 ecpublickey publickey = (ecpublickey) keypair.getpublic(); // 私钥 ecprivatekey privatekey = (ecprivatekey) keypair.getprivate(); // 根据公钥参数生成曲线 curve curve = curve.forecparameterspec(publickey.getparams()); // 构建ec加密key return new eckey.builder(curve, publickey) .privatekey(privatekey) .keyid(uuid.randomuuid().tostring()) .build(); } /** * 生成hmacsha256密钥 */ public static octetsequencekey generatesecret() { secretkey secretkey = keygeneratorutils.generatesecretkey(); return new octetsequencekey.builder(secretkey) .keyid(uuid.randomuuid().tostring()) .build(); }} class keygeneratorutils { private keygeneratorutils() { } /** * 生成rsa密钥 */ static keypair generatersakey() { keypair keypair; try { keypairgenerator keypairgenerator = keypairgenerator.getinstance(rsa); keypairgenerator.initialize(2048); keypair = keypairgenerator.generatekeypair(); } catch (exception ex) { throw new illegalstateexception(ex); } return keypair; } /** * 生成ec密钥 */ static keypair generateeckey() { ellipticcurve ellipticcurve = new ellipticcurve( new ecfieldfp( new biginteger(115792089210356248762697446949407573530086143415290314195533631308867097853951)), new biginteger(115792089210356248762697446949407573530086143415290314195533631308867097853948), new biginteger(41058363725152142129326129780047268409114441015993725554835256314039467401291)); ecpoint ecpoint = new ecpoint( new biginteger(48439561293906451759052585252797914202762949526041747995844080717082404635286), new biginteger(36134250956749795798585127919587881956611106672985015071877198253568414405109)); ecparameterspec ecparameterspec = new ecparameterspec( ellipticcurve, ecpoint, new biginteger(115792089210356248762697446949407573529996955224135760342422259061068512044369), 1); keypair keypair; try { keypairgenerator keypairgenerator = keypairgenerator.getinstance(ec); keypairgenerator.initialize(ecparameterspec); keypair = keypairgenerator.generatekeypair(); } catch (exception ex) { throw new illegalstateexception(ex); } return keypair; } /** * 生成hmacsha256密钥 */ static secretkey generatesecretkey() { secretkey hmackey; try { hmackey = keygenerator.getinstance(hmacsha256).generatekey(); } catch (exception ex) { throw new illegalstateexception(ex); } return hmackey; }} 新建 consentcontroller,编写登录和认证页面的跳转
如果在上面没有使用自定义的登录和授权页面,下面的跳转方法按需舍去
@slf4j@controllerpublic class consentcontroller { private final registeredclientrepository registeredclientrepository; private final oauth2authorizationconsentservice authorizationconsentservice; public consentcontroller(registeredclientrepository registeredclientrepository, oauth2authorizationconsentservice authorizationconsentservice) { this.registeredclientrepository = registeredclientrepository; this.authorizationconsentservice = authorizationconsentservice; } @responsebody @getmapping(/favicon.ico) public string faviconico(){ return favicon.ico; } @getmapping(/login) public string loginpage(){ return login; } @getmapping(value = /oauth2/consent) public string consent(principal principal, model model, @requestparam(oauth2parameternames.client_id) string clientid, @requestparam(oauth2parameternames.scope) string scope, @requestparam(oauth2parameternames.state) string state) { // remove scopes that were already approved set scopestoapprove = new hashset(); set previouslyapprovedscopes = new hashset(); registeredclient registeredclient = this.registeredclientrepository.findbyclientid(clientid); oauth2authorizationconsent currentauthorizationconsent = this.authorizationconsentservice.findbyid(registeredclient.getid(), principal.getname()); set authorizedscopes; if (currentauthorizationconsent != null) { authorizedscopes = currentauthorizationconsent.getscopes(); } else { authorizedscopes = collections.emptyset(); } for (string requestedscope : stringutils.delimitedlisttostringarray(scope, )) { if (authorizedscopes.contains(requestedscope)) { previouslyapprovedscopes.add(requestedscope); } else { scopestoapprove.add(requestedscope); } } model.addattribute(clientid, clientid); model.addattribute(state, state); model.addattribute(scopes, withdescription(scopestoapprove)); model.addattribute(previouslyapprovedscopes, withdescription(previouslyapprovedscopes)); model.addattribute(principalname, principal.getname()); return consent; } private static set withdescription(set scopes) { set scopewithdescriptions = new hashset(); for (string scope : scopes) { scopewithdescriptions.add(new scopewithdescription(scope)); } return scopewithdescriptions; } public static class scopewithdescription { private static final string default_description = unknown scope - we cannot provide information about this permission, use caution when granting this.; private static final map scopedescriptions = new hashmap(); static { scopedescriptions.put( message.read, this application will be able to read your message. ); scopedescriptions.put( message.write, this application will be able to add new messages. it will also be able to edit and delete existing messages. ); scopedescriptions.put( other.scope, this is another scope example of a scope description. ); } public final string scope; public final string description; scopewithdescription(string scope) { this.scope = scope; this.description = scopedescriptions.getordefault(scope, default_description); } } } 新建 usercontroller,user,userservice等标准的自定义用户业务,此处仅放出userserviceimpl
@requiredargsconstructor@slf4j@componentclass userserviceimpl implements userservice { private final usermapper usermapper; @override public userdetails loaduserbyusername(string username) throws usernamenotfoundexception { user user = usermapper.selectone(new lambdaquerywrapper().eq(user::getusername,username)); return new org.springframework.security.core.userdetails.user(username, user.getpassword(), new arraylist()); }} 启动项目,如下图
认证服务器整体结构图
资源服务器相关配置 pom.xml引入资源服务器相关依赖
org.springframework.boot spring-boot-starter-oauth2-resource-server org.springframework.boot spring-boot-starter-security 新增配置文件 application.yaml
server: port: 9003spring: application: name: resource security: oauth2: resourceserver: jwt: issuer-uri: http://127.0.0.1:9000feign: client: config: default: #配置超时时间 connect-timeout: 10000 read-timeout: 10000 新增资源服务器配置文件 resourceserverconfiguration
@configuration@enablewebsecurity(debug = true)@enableglobalmethodsecurity(prepostenabled = true) //开启鉴权服务public class resourceserverconfiguration { @bean public securityfilterchain httpsecurityfilterchain(httpsecurity httpsecurity) throws exception { // 所有请求都进行拦截 httpsecurity.authorizerequests().anyrequest().authenticated(); // 关闭session httpsecurity.sessionmanagement().disable(); // 配置资源服务器的无权限,无认证拦截器等 以及jwt验证 httpsecurity.oauth2resourceserver() .accessdeniedhandler(new simpleaccessdeniedhandler()) .authenticationentrypoint(new simpleauthenticationentrypoint()) .jwt(); return httpsecurity.build(); } } 新增相关无认证无权限统一拦截回复 simpleaccessdeniedhandler 和 simpleauthenticationentrypoint
/** * 携带了token 而且token合法 但是权限不足以访问其请求的资源 403 * @author zxg */public class simpleaccessdeniedhandler implements accessdeniedhandler { @override public void handle(httpservletrequest request, httpservletresponse response, accessdeniedexception accessdeniedexception) throws ioexception, servletexception { response.setstatus(httpservletresponse.sc_forbidden); response.setcharacterencoding(utf-8); response.setcontenttype(mediatype.application_json_value); objectmapper objectmapper = new objectmapper(); string resbody = objectmapper.writevalueasstring(singleresultbundle.failed(无权访问)); printwriter printwriter = response.getwriter(); printwriter.print(resbody); printwriter.flush(); printwriter.close(); }} /** * 在资源服务器中 不携带token 或者token无效 401 * @author zxg */@slf4jpublic class simpleauthenticationentrypoint implements authenticationentrypoint { @override public void commence(httpservletrequest request, httpservletresponse response, authenticationexception authexception) throws ioexception, servletexception { if (response.iscommitted()){ return; } throwable throwable = authexception.fillinstacktrace(); string errormessage = 认证失败; if (throwable instanceof badcredentialsexception){ errormessage = 错误的客户端信息; }else { throwable cause = authexception.getcause(); if (cause instanceof jwtvalidationexception) { log.warn(jwt token 过期,具体内容: + cause.getmessage()); errormessage = 无效的token信息; } else if (cause instanceof badjwtexception){ log.warn(jwt 签名异常,具体内容: + cause.getmessage()); errormessage = 无效的token信息; } else if (cause instanceof accountexpiredexception){ errormessage = 账户已过期; } else if (cause instanceof lockedexception){ errormessage = 账户已被锁定;// } else if (cause instanceof invalidclientexception || cause instanceof badclientcredentialsexception){// response.getwriter().write(json.tojsonstring(singleresultbundle.failed(401,无效的客户端)));// } else if (cause instanceof invalidgrantexception || cause instanceof redirectmismatchexception){// response.getwriter().write(json.tojsonstring(singleresultbundle.failed(无效的类型)));// } else if (cause instanceof unauthorizedclientexception) {// response.getwriter().write(json.tojsonstring(singleresultbundle.failed(未经授权的客户端))); } else if (throwable instanceof insufficientauthenticationexception) { string message = throwable.getmessage(); if (message.contains(invalid token does not contain resource id)){ errormessage = 未经授权的资源服务器; }else if (message.contains(full authentication is required to access this resource)){ errormessage = 缺少验证信息; } }else { errormessage = 验证异常; } } response.setstatus(httpservletresponse.sc_unauthorized); response.setcharacterencoding(utf-8); response.setcontenttype(mediatype.application_json_value); objectmapper objectmapper = new objectmapper(); string resbody = objectmapper.writevalueasstring(singleresultbundle.failed(errormessage)); printwriter printwriter = response.getwriter(); printwriter.print(resbody); printwriter.flush(); printwriter.close(); }} 新增 resourcecontroller 进行接口测试
@slf4j@restcontrollerpublic class resourcecontroller { /** * 测试spring authorization server,测试权限 */ @preauthorize(hasauthority('scope_message.read')) @getmapping(/gettest) public string gettest(){ return gettest; } /** * 默认登录成功跳转页为 / 防止404状态 * * @return the map */ @getmapping(/) public map index() { return collections.singletonmap(msg, login success!); } @getmapping(/getresourcetest) public singleresultbundle getresourcetest(){ return singleresultbundle.success(这是resource的测试方法 getresourcetest()); }} 启动项目,效果如下
项目总体结构如下
测试认证鉴权 #调用 /oauth2/authorize ,获取codehttp://127.0.0.1:9000/oauth2/authorize?client_id=zxg&response_type=code&scope=message.read&redirect_uri=http://www.baidu.com#会判断是否登录,若没有,则跳转到登录页面,如下图1#登录完成后,会提示是否授权,若没有,则跳转到授权界面,如下图2#授权成功后,跳转到回调地址,并带上code,如图3 打开postman,进行获取access_token
#访问 /oauth2/token 地址#在authorization中选择basic auth模式,填入对应客户端,其会在header中生成authorization,如下图右侧 返回结果如下
调用resourcecontroller中的接口,测试token是否生效
源码下载地址
https://gitee.com/rjj521/authorization-server-learn 总结 至此,spring-authorization-server的基础使用已完成,总体上和原spring security oauth大差不差,个别配置项不同。期间在网上搜寻了很多资料,然后进行整合,因此文中存在与其他网上教程相同代码,如有争议,请联系我删除改正,谢谢。
新唐科技N572F072简介
利用藻类和磁粒子开发微型机器人,可传递药物和远距引导
n沟道mos管和p沟道mos管详解
浅谈电子连接器对尼龙材质的要求
中芯国际可能研发出14nm工艺 台媒:三星在坏中芯国际的好事
为什么使用spring-authorization-server?
点成动态丨 2023中国(南京)国际科教技术及装备博览会(CESEE)精彩回顾
电子线对我们有着非常巨大的实际性价值
当3D打印应用在无人机有何不同?
STM32复位来源(寄存器版)
华硕灵珑ASUSPRO评测 算是出类拔萃的优秀产品了
热敏电阻和热电阻有什么区别
PCB设计中焊盘的设计标准解析
电能质量分析仪可用电压基准源GREF10XX,10ppm/°C温漂
基于FPGA的磁浮轴承控制系统研究
安士佳携手亿智电子共同推动AI芯片在万物智联领域的应用
福布斯中国发布最佳CEO排名 比亚迪王传福和宁德时代曾毓群上榜
IFA2019,将有哪些趋势性的技术亮点呈现?
设备拆卸的基本原则及常用方法
Borosil推出了一种名为“ Suraksha”的UV-C消毒设备