// 今天主要讲这么几个问题
1.如何保证api接口合理安全调用
2.oauth2.0授权认证平台设计原理
3.oauth2.0认证协议的四种模式
4.oauth2.0实现对api接口保护
https://www.cnblogs.com/blowing00/p/4521135.html
看大佬的讲解吧
我的api 如果让很多合作伙伴访问 请问怎么做
合作伙伴可能会调用到我的接口,此时怎么保证我的开放接口的安全性
我吧我的api 提供给合作伙伴公司,我该如何管理我这个开放接口
这些合作伙伴想要调用我接口,必须要求 申请一个appKey和这个secret 用appkey和密码换取access_token,有了这个access_token 才可以访问我接口
oauth2.0授权协议
// oauth2.0 有哪几种
授权码> 获取access_token通过access_token你才能调用我接口
密码> 你在调用我接口的时候传userName和password
简化模式
客户端模式
调用接口传递令牌(access_token) 为什么比较安全
因为令牌是临时且唯一的,有时间限制的
假设现在我们公司接口需要被很多合作伙伴调用的情况西
首先我先搭建一个 认证管理web 平台
就是说你要在我平台申请一个appid和appkey
oauth2.0协议授权模式
1.合作伙伴首先先要申请appid和appkey 区分不同的合作伙伴[appid 是终身不会发生变化,appkey是可以修改的]
2.合作伙伴用这个appid和appkey申请一个授权code (认证授权获取授权码code),
3.拿到code获取 accesstoken,再然后拿到这个access_token 才可以掉我接口
我们来看下知乎的微信扫码登录吧
https://open.weixin.qq.com/connect/qrconnect?
appid=wx268fcfe924dcb171
&redirect_uri=https%3A%2F%2Fwww.zhihu.com%2Foauth%2Fcallback%2Fwechat%3Faction%3Dlogin%26from%3D
&response_type=code
&scope=snsapi_login
&state=59346f327367534969737557456a5a556e315555325235576e425a5a384f4430#wechat
首先第一步 他会生成一个授权连接,获取授权码code
// 我们可以对比看看微信得官网
https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
嗯,是不是一样的,当我们点击同意的时候,回生成一个code 来回调我们的redirect_uri
只不过这里页面跳转的太快 我就不演示了
第二步, 后台回用这个code 来生成一个access_token, 意思是我只要拿到这个accessToken就可以调用接口
第三步:根据这个accessToken 此时就可以调用微信接口了,可以获取用户的openId[此时这个openID是腾讯对qq账户设置一个开放的userId,而不是真实的userId]
第四步 根据用户的openId获取用户的信息
accesstoken 有效期 2小时 如果这个accesstoken 过期可以去使用refresh_token去获取最新的access_token
oauth2.0协议角色划分
我们再来看这张图 oauth2.0角色的划分无非就是2个角色
一个AuthenticationService【认证授权中心】 认证授权,根据appkey 获取授权码,accesstoken
一个ResourceService【资源管理器】 被开放的接口检查
然后我们先通过Springsecurity搭建一个AuthenticationService
package com.lvhao.auth.service.config;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
// 配置登录账号密码
@Component
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated() // 所有请求都要通过认证
.and()
.httpBasic()
.and()
.csrf().disable();// 关闭跨域保护
}
// 配置登录账号密码
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("mayiketi")
.password(passwordEncoder().encode("123456"))
.authorities("/*");
}
}
package com.lvhao.auth.service.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.stereotype.Component;
//
@Component
@EnableAuthorizationServer// 当前项目是一个认证授权中心 开启认证授权
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
PasswordEncoder passwordEncoder;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// 检查accesstoken权限
//允许表单提交,检查accesstoken是否有效的情况下 必须加配置 权限放开
security.allowFormAuthenticationForClients()
.checkTokenAccess("permitAll()");
}
/*
* 分配我的appid和appkey
* */
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
/*appid*/
.withClient("mayikt_appid")
/*密钥*/
.secret(passwordEncoder.encode( "mayikt_pwd"))
.authorizedGrantTypes("authorization_code")
/*所有的接口都可以方位*/
/*member-service表示只能访问到我的member服务接口不能访问别的接口*/
//分配appid调用接口的权限
.scopes("all")
.resourceIds("mayikt_resource")
// 用户选择授权之后跳转到该地址传递code授权码
.redirectUris("https://www.baidu.com/");
}
}
我此时的回调地址配置的百度 当点击授权的时候 此时会跳到回调地址上返回一个code
然后第二步就是 用这个code 来换取access_token
http://localhost:9000/oauth/token?code=2rX4gF&grant_type=authorization_code
&redirect_uri=https://www.baidu.com/
&scope=all
&client_id=mayikt_appid
&client_secret=mayikt_pwd
看到了吧 根据此时这code 就可以拿到access_token
我们下一步可以检查这个token 是否有效
http://localhost:9000/oauth/check_token?token=787cb830-71b6-4c4d-a7a8-861145efb075
------------------------------------------------?
嗯 我们的认证管理器就搭建完毕了, 然后我们现在搭建我们的资源管理器
@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {
@Value("mayikt_appid")
private String mayiktAppid;
@Value("mayikt_pwd")
private String mayiktAppSecret;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 去拦截请求判断有没有去传递accesstoken 同时检查accesstoken 是否有效
@Primary
@Bean
public RemoteTokenServices remoteTokenServices() {
final RemoteTokenServices tokenServices = new RemoteTokenServices();
//设置授权服务器cache_token 的完整地址 http://localhost:9000/oauth/check_token
tokenServices.setCheckTokenEndpointUrl("http://localhost:9000/oauth/check_token");
//APPID
tokenServices.setClientId(mayiktAppid);
//KEY
tokenServices.setClientSecret(mayiktAppSecret);
return tokenServices;
}
@Override
public void configure(HttpSecurity http) throws Exception {
//设置创建session策略
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
http.authorizeRequests().anyRequest().authenticated();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("mayikt_resource").stateless(true);
}
}
每当访问资源管理器的时候, 或者调用api的时候,资源管理器首先第一步操作就是调用这个check 来看此时的access_token 是否有效
我有个这么个接口,如果此时不携带access_token 的话就会报错异常
看此时携带这个token 的话就不会有异常了 ,并且可以正常访问了
我们也可以用 jwt的形式 通过 Bearer+code 写在头中
嗯 最后一个问题 就是互联网架构中我们的接口安全如何去做