单点登录和第三方登录+JWT生成token+阿里云短信服务+OAuth2(微信登录)
阿里云
域名分级
com,cn为一级域名(顶级域名)
com.cn,baidu.com二级域名
sina.com.cn/tieba.baidu.com为三级域名
使用者一般把可支持独立备案的主域名称作一级域名,如 baidu.com、sina.com.cn 皆可称作一级域名,在主域名下建立的直接子域名称作二级域名,如 tieba.baidu.com 为二级域名。
单点登录的三种实现方式
实现单点登录的关键在于,如何让 Session ID(或 Token)在多个域中共享
方式一:父域Cookie
Cookie的作用域由domain属性和path属性共同决定的。
domain属性的有效值为当前域或其父域的域名/IP地址,在Tomcat中domain属性默认为当前域的域名/IP地址。
path属性的有效值是以"/"开头的路径,在tomcat中path属性默认为当前Web应用的上下文路径
如果将 Cookie 的 domain 属性设置为当前域的父域,那么就认为它是父域 Cookie。Cookie 有一个特点,即父域中的 Cookie 被子域所共享,换言之,子域会自动继承父域中的 Cookie。
利用 Cookie 的这个特点,不难想到,将 Session ID(或 Token)保存到父域中不就行了。没错,我们只需要将 Cookie 的 domain 属性设置为父域的域名(主域名),同时将 Cookie 的 path 属性设置为根路径,这样所有的子域应用就都可以访问到这个 Cookie 了。不过这要求应用系统的域名需建立在一个共同的主域名之下,如 tieba.baidu.com 和 map.baidu.com,它们都建立在 baidu.com 这个主域名之下,那么它们就可以通过这种方式来实现单点登录。
总结:此种实现方式比较简单,但不支持跨主域名。
方式二:认证中心
认证中心就是一个专门负责处理登录请求的独立的 Web 服务。
用户统一在认证中心进行登录,登录成功后,认证中心记录用户的登录状态,并将 Token 写入 Cookie。(注意这个 Cookie 是认证中心的,应用系统是访问不到的。)
应用系统检查当前请求有没有 Token,如果没有,说明用户在当前系统中尚未登录,那么就将页面跳转至认证中心。由于这个操作会将认证中心的 Cookie 自动带过去,因此,认证中心能够根据 Cookie 知道用户是否已经登录过了。如果认证中心发现用户尚未登录,则返回登录页面,等待用户登录,如果发现用户已经登录过了,就不会让用户再次登录了,而是会跳转回目标 URL ,并在跳转前生成一个 Token,拼接在目标 URL 的后面,回传给目标应用系统。
应用系统拿到 Token 之后,还需要向认证中心确认下 Token 的合法性,防止用户伪造。确认无误后,应用系统记录用户的登录状态,并将 Token 写入 Cookie,然后给本次访问放行。(注意这个 Cookie 是当前应用系统的,其他应用系统是访问不到的。)当用户再次访问当前应用系统时,就会自动带上这个 Token,应用系统验证 Token 发现用户已登录,于是就不会有认证中心什么事了。
这里顺便介绍两款认证中心的开源实现:
- Apereo CAS 是一个企业级单点登录系统,其中 CAS 的意思是”Central Authentication Service“。它最初是耶鲁大学实验室的项目,后来转让给了 JASIG 组织,项目更名为 JASIG CAS,后来该组织并入了 Apereo 基金会,项目也随之更名为 Apereo CAS。
- XXL-SSO 是一个简易的单点登录系统,由大众点评工程师许雪里个人开发,代码比较简单,没有做安全控制,因而不推荐直接应用在项目中,这里列出来仅供参考。
总结:此种实现方式相对复杂,支持跨域,扩展性好,是单点登录的标准做法。
方式三:LocalStorage 跨域
浏览器对 Cookie 的跨域限制越来越严格。Chrome 浏览器还给 Cookie 新增了一个 SameSite 属性,此举几乎禁止了一切跨域请求的 Cookie 传递(超链接除外),并且只有当使用 HTTPs 协议时,才有可能被允许在 AJAX 跨域请求中接受服务器传来的 Cookie
不过,在前后端分离的情况下,完全可以不使用 Cookie,我们可以选择将 Session ID (或 Token )保存到浏览器的 LocalStorage 中,让前端在每次向后端发送请求时,主动将 LocalStorage 的数据传递给服务端。这些都是由前端来控制的,后端需要做的仅仅是在用户登录成功后,将 Session ID (或 Token )放在响应体中传递给前端。
在这样的场景下,单点登录完全可以在前端实现。前端拿到 Session ID (或 Token )后,除了将它写入自己的 LocalStorage 中之外,还可以通过特殊手段将它写入多个其他域下的 LocalStorage 中。
//获取token
var token = result.data.token;
//动态创建一个不可见的iframe,在其中加载一个跨域HTML
var iframe = document.createElement("iframe");
iframe.src = "http://app1.com/localstorage.html";
document.body.append(iframe);
//使用postMessage()方法将token传递给iframe
setTimeout(function(){
iframe.contentWindow.postMessage(token,"http://app1.com");
},4000);
setTimeout(function(){
iframe.remove();
},6000);
// 在这个iframe所加载的HTML中绑定一个事件监听器,当事件被触发时,把接收到的token数据写入localStorage
window.addEventListener('message',function(){
localStorage.getItem('token',enent.data);
},false);
前端通过 iframe+postMessage() 方式,将同一份 Token 写入到了多个域下的 LocalStorage 中,前端每次在向后端发送请求之前,都会主动从 LocalStorage 中读取 Token 并在请求中携带,这样就实现了同一份 Token 被多个域所共享。
总结:此种实现方式完全由前端控制,几乎不需要后端参与,同样支持跨域。
==========================================
第三方多账号登录

数据库设计
引入第三方账户方案
这里是以QQ-SDK的登录逻辑, 我们先来一波时序图:
-
客户端自己调起登录的界面,进行输入用户名、密码,这里的是第三方的用户名,密码,登录成功后,会返回access_token openid expire_in,这过程会使用到oauth2.0,不过在sdk里面进行内置回调获取了,后面我们会说明我们自身实现的oauth2.0
-
客户端拿到access_token、openid、login_type(qq、wechat…)请求应用服务器,应用服务器拿到这些数据后就会根据对应的login_type去对应的用户中心进行access_token和openid进行校验。
校验不通过则返回对应错误码
-
校验通过后就会判断本地是否有这个login_type和openid是否存在,不存在则进行获取远程的用户名、头像等基础信息来作为本地基础数据,并且返回code值
-
如果已经存在,那就是进行登录操作,返回code值。
-
客户端拿到code值后进行token值的换取,这个完全遵照oauth2.0的协议来走的,后续每次请求必须带上token,token值在服务端的时间比较久,因为我们想要做的是那种永不下线的操作,所以每次请求我们都将token过期时间进行累加。
数据库设计
用户基础表(users):
用户验证关联表(user_auth_rel)
本地用户表
第三方用户表
-
users表只是单纯针对我们业务侧的登录,主要是做自身业务的oauth2.0业务,
-
user_local_auth是做自己用户名、密码登录,手机号码登录信息记录,
-
user_third_auth是我们第三方用户体系的数据记录,
-
user_auth_rel是用来关联我们users表与user_local_auth、user_third_auth。
-
整个设计理念就是将自建用户与第三方在存储上区分,这在架构演进上也是合乎情理的,开始用户体系大多自建,而后才是对外接入。
总结
总的来讲,第三方用户的接入技术上来讲是比较简单的,这里设计多一个user_thirds是可以支持足够多的第三方接入,当然一般我们也就两三个登录就好,太多登录方不仅自身维护成本,界面摆盘也不好看不是。
========================================================
项目实战
两种登录方式:
- 手机号码+手机验证码(若数据库存在直接登录,若不存在自动注册)
- 微信扫描

一、创建模块
配置文件application.propertis
server.port=8203
spring.application.name=service-user
spring.profiles.active=dev
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url="jdbc:mysql://xxxx/yygh_user?..."
spring.datasource.username=root
spring.datasource.password=123
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zond=GMT+8
#nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#配置mapper.xml文件路几个
mybatis-plus.mapper-locations=classpath:com/xxx/*.xml
启动类
@SpringBootApplication
@ComponentScan(basePackages="com.michael")
@EnableDiscoveryClient
@EnableFeignClients(basePackages="com.michael")
public class ServiceUserApplication{
public static void main(String[] args){
SpringApplication.run(ServiceUserApplication.class,args)
}
}
在网关gateway模块中的配置文件中,配置路由
spring.cloud.gateway.routes[2].id=service-user # 对应注册中心登录模块的服务名
spring.cloud.gateway.routes[2].uri=lb://service-user #设置路由uri
spring.cloud.gateway.routes[2].predicates=Path=/*/user/** #设置路由断言
spring.cloud.gateway.routes[3].id=service-sms # 对应注册中心登录模块的服务名
spring.cloud.gateway.routes[3].uri=lb://service-sms #设置路由uri
spring.cloud.gateway.routes[3].predicates=Path=/*/sms/** #设置路由断言
二、创建业务类
其中实体类已经在common模块中
Controller
@RestController
@RequestMapping("/api/user")
public class UserInfoApiController{
@Autowired
private UserInfoService userInfoService;
//用手机号登录接口
@PostMapping("login")
public Result login(@RequestBody LoginVo loginVo){
Map<String,Object> info = userInfoService.loginUser(loginVo);
return Result.ok(info);
}
}
Service
public interface UserInfoService extends IService<UserInfo>{
Map<String,Object> loginUser(LoginVo loginVo);
}
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper,UserInfo> implemeents UserInfoService{
@Autowired
private RedisTemplate<String,String> redisTemplate;
@Override
public Map<String,Object> loginUser(LoginVo loginVo){
//1.获取用户输入的手机号和验证码
String phone = loginVo.getPhone();
String code = loginVo.getCode();
//2.判断手机号和验证码是否为空
if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)){
throw new YyghException(ResultCodeEnum.PARAM_ERROR);
}
//3.判断手机验证码和输入的验证码是否一致
String redisCode = redisTemplate.opsForValue().get(phone);
if(!code.equals(redisCode)){
throw new YhghException(ResultCodeEum.PARAM_ERROR);
}
//4.判断是否是第一次登录,根据手机号查询数据据,如果不存在相同手机号就是第一次登录,直接注册
QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
wrapper.eq("phone",phone);
UserInfo userInfo = baseMapper.selectOne(wrapper);
if(userInfo == null){//如果为空,添加到数据库
userInfo = new UerInfo();
userInfo.setName("");
userInfo.setPhone(phone);
userInfo.setStatus(1);
baseMapper.insert(userInfo);
}
//如果不为空,判断状态是否为禁用
if(userInfo.getStatus() == 0){
throw new YyghException(ResultCodeEnum.LOGIN_DISABLED_ERROR);
}
//如果数据库已经存在(即注册过)
Map<String,Object> map = new HashMap<>();
String name = userInfo.getName();
if(StringUtils.isEmpty(name)){
name = userInfo.getNickName();
}
if(StringUtils.isEmpty(name)){
name = userInfo.getPhone();
}
map.put("name",name);
//将从数据库获取的用户id和用户名生成token
String token = JwtHelper.crateToken(userInfo.getId(),name);
map.put("token",token);
return map;
}
}
Mapper(MyBatisPlus)
public interface UserInfoMapper extends BaseMapper<UserInfo>{
}
Mapper.xml文件
配置类
@Configuraiton
@MapperScan("com.michael.yygh.user.mapper")
public class UserConfig{
}
JWT生成Token
依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt<artifactId>
</dependency>
工具类
public class JwtHelper{
private static long tokenExpiration = 24*60*60*1000;//过期时间
private static String tokenSignKey = "123456";//签名密钥
public static String createToken(long userId,String userName){
String token = Jwts.builder()
.setSubject("YYGH-USER")
.setExpiration(new Date(System.currentTimeMillis()+tokenExpiration))
.claim("userId",userId)
.claim("userName",uerName)
.signWith(SignatureAlgorithm.HS512,tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
//根据token字符串得到用户id
public static Long getUserId(String token){
if(StringUtils.isEmpty(token)){
return null;
}
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
Integer userId = (Integer)claims.get("userId");
return userId.longValue();
}
//根据token字符串获取用户名
public static String getUserName(String token){
if(StringUtils.isEmpty(token)){
return "";
}
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return (String)claims.get("userName");
}
}
阿里云短信服务
一:免费开通,进入页面
签名管理

模板管理

二:创建AccessKey


①、创建service-msm模块
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
application.properties配置文件
server.port=8204
spring.application.name=service-msm
#返回json的全局时间格式
Spring.jackson.data-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
Spring.redis.host=192.168.44.165
spring.redis.port=6379
spring.redis.database=0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1 #最大阻塞时间,负数表示没有限制
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
spring.cloud.nacos.discovery.server-addr=127.0.0.1
aliyun.sms.regionId=default
aliyun.sms.accessKeyId=
aliyun.sms.secret=
启动类
@SpringBootApplication(exclude=DataSourceAutoConfiguration.class)//取消数据源自动配置
@EnableDiscoveryClient
@ComponentScan(baskPackages={"com.michael"})
public class ServiceMsmApplication{
public static void main(String[] args){
SpringApplication.run(ServiceMsmApplication.class,args);
}
}
配置网关
在这里插入代码片
②、工具类读取配置文件中阿里云相关配置
@Component
public class ConstantPropertiesUtils implements InitializingBean{
@Value("${aliyun.sms.regionId}")
private String regionId;
@Value("${aliyun.sms.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.sms.secret}")
private String secret;
//设置三个常量用于对外使用
public static String REGION_ID;
public static String ACCESS_KEY_ID;
public Static String SECRECT;
@Override
public void afterPropertiesSet()throws Exception{
REGION_ID = regionId;
ACCESS_KEY_ID = accessKeyId;
SECRECT = secret;
}
}
③、业务类
Controller
@RestController
@RequestMapping("/api/msm")
public class MsmApiController{
@Autowired
private MsmService msmService;
@Autowired
private RedisTemplate<String,String> redisTemplate;
//发送手机验证码
@GetMapping("send/{phone}")
public Result sendCode(@PathVariable String phone){
//从redis先去获取验证码,判断是否能获取得到
String code = redisTemplate.opsForValue().get(phone);
if(!StringUtils.isEmpty(code)){
return Result.ok();
}
//若获取不到redis中的验证码,则生成新的验证码并整合阿里短信发送,然后将生成的验证码放入redis
code = RandomUtil.getSixBitRandom();
boolean isSend = msmService.send(phone,code);
if(isSend){
redisTemplate.opsForValue().set(phone,code,2,TimeUnit.MINUTES);
return Result.ok();
}else{
return Result.fail().message("发送短信失败");
}
}
}
Service
public interface MsmService{
boolean send(String phone,String code);
}
@Service
public class MsmServiceImpl implements MsmService{
@Override
public boolean send(String phone,String code){
//判断手机号是否为空
if(StringUtils.isEmpty(phone)){
return false;
}
//整合阿里云短信服务
DefaultProfile profile = DefaultProfile.getProfile(ConstantPropertiesUtils.REGION_Id,
ConstantPropertiesUtils.ACCESS_KEY_ID,
ConstantPropertiesUtils.SECRECT);
IAcsClient client = new DefaultAcsClient(profile);
CommonRequest request = new CommonRequest();
request.setProtocol(ProtocolType.HTTPS);
request.setMethod(MethodType.POST);
request.setDomain("dysmsapi.aliyuncs.com");
request.setVersion("2023-11-1");
request.setAction("SendSms");
//参数左边是固定,右边是自定义
request.putQueryParameter("PhoneNumbers",PhoneNumbers);
request.putQueryParameter("SignName","双十一");//与阿里申请时候的一致
request.putQueryParameter("TemplateCode","SMS_180051135");
//验证码使用json格式
Map<String,Object> param = new HashMap();
param.put("code",code);
request.putQueryParameter("TemplateParam",JSONObject.toJSONString(param));
//调用方法进行发送
try{
CommonResponse response = client.getCommonResponse(request);
System.out.println(response.getData());
return response.getHttpResponse().isSuccess();
}catch(ServerException e){
e.printStackTrace();
}catch(ClientException e){
e.printStackTrace();
}
return false;
}
}
生成验证码的工具类
public class RandomUtil{
private static final Random random = new Random();
private static final DecimalFormat fourdf = new DecimalFormat("0000");
private static final DecimalFormat sixdf = new DecimalFromat("000000");
public static String getFourBitRandom(){
return fourdf.format(random.nextInt(10000));
}
public static String getSixBitRandom(){
return sixdf.format(random.nextInt(1000000));
}
public static ArrayList getRandom(List list,int n){
Random random = new Random();
HashMap<Object,Object> hashMap = new HashMap<Object,Object>();
//生成随机数并存入HashMap
for(int i = 0;i < list.size();i++){
int number = random.nextInt(100) + 1;
hashMap.put(number,i);
}
//从HashMap导入数组
Object[] robjs = hashMap.values().toArray();
ArrayList r = new ArrayList();
//遍历数组并打印数据
for(int i = 0;i < n;i++){
r.add(list.get((int)robjs[i]));
System.out.print(list.get((int)robjs[i]) + "\t");
}
System.out.print("\n");
return r;
}
}
============================
前端接口调用
api目录下:
userInfo.js
import request from '@/utils/request'
const api_name = '/api/user'
export dafault{
login(userInfo){
return request({
url:'${api_name}/login',
method:'post',
data:userInfo
})
}
}
msm.js
import request.from '@utils/request'
const api_name = '/api/msm'
export default{
sendCode(mobile){
return request({
url:'${api_name}/send/${mobile}',
method:'get'
})
}
}
layouts目录下
myheader.vue(需要安装cookie)
npm install js-cookie
<!--微信登陆和手机登录-->
<script>
import cookie from 'js-cookie'
import Vue from 'vue'
import userInfoApi from '@/api/userInfo'
import smsApi from '@/api/msm'
import hospitalApi from '@/api/hosp'
//定义常量
const defaultDialogAtrr = {
showLoginType:'phone',//控制手机登录与微信登录切换
labelTips:'手机号码',//输入框提示
inputValue:'',//输入框绑定对象
placeholder:'请输入您的手机号',
maxlength:11,//输入框长度控制
loginBtn:'获取验证码',//登录按钮或获取验证码按钮文本
sending:true,//是否可以发送验证码
second:-1,//倒计时间second>0:显示倒计时 second=0:重新发送second=-1
clearSmsTime:null //倒计时定时任务引用,关闭登录层清除定时任务
}
export default{
data(){
return{
userInfo:{},
dialogUserFormVisible:false,
dialogAtrr:defaultDialogAtrr,//弹出层相关属性
name:''//用户登录显示的名称
}
},
created(){
this.showInfo()
},
methods:{
//绑定登录或获取验证码按钮
if(this.dialogAtrr.loginBtn == '获取验证码'){
}
}
}
</script>
OAuth2
解决方案:
一、开放系统间授权问题(用户授权第三方应用去访问受保护的资源)
方式①:用户名密码复制
方式②:通用开发者key
方式③:颁发令牌
考虑如何管理令牌,颁发令牌,吊销令牌需要统一协议,就有了Oauth2协议
二、单点登录问题
在其中一个模块登录,若访问其他模块不需要登录
微信登录

一、准备工作
①、微信开放平台注册 https://open.weixin.qq.com
②、邮箱激活
③、完善开发者资料
④、开发者资质认证(准备营业执照1-2个工作日审批、300元)
⑤、创建网站应用(提交审核,需要7个工作日审批)
配置文件
添加组件工具类读取配置文件
@Component
public class ConstantPropertiesUtil implements InitializingBean{
@Value("${wx.open.app_id}")
private String appId;
@Value("${wx.open.app_secret}")
public String appSecret;
@Value("${wx.open.redirect_url}")
private String redirectUrl;
@Value("${yygh.baseUrl}")//前端项目url
private String yyghBaseUrl;
public static String WX_OPEN_APP_ID;
public static String WX_OPEN_APP_SECRET;
public static String WX_OPEN_REDIRECT_URL;
public static String YYGH_BASE_URL;
@Override
public void afterPropetiesSet() throws Exception{
REGION_ID = regionId;
ACCESS_KEY_ID = accessKeyId;
SECRECT = secret;
}
}
二、授权流程
Controller
@Controller //用来跳转页面,而@RestController用来返回数据
@RequestMapping("/api/ucenter/wx")
public class WeixinApiController{
//生成微信扫描二维码,返回生成二维码需要的参数
@GetMapping("getLoginParam")
@ResponseBody
public Result getQrConnect(){
try{
Map<String,Object> map = new HashMap<>();
map.put("appid",ConstantWxPropertiesUtils.WX_OPEN_APP_ID);
map.put("scope","snsapi_login");
String wxOpenRedirectUrl = ConstantWxPropertiesUtils.WX_OPEN_REDIRECT_URL;
wxOpenRedirectUrl = URLEncoder.encode(wxOpenRedirectUrl,"utf-8");
map.put("redirect_uri","snsapi_login");
map.put("redirect_uri",wxOpenRedirectUrl);
map.put("state",System.currentTimeMillis()+"");
return Result.ok(map);
}catch(UnsupportedEncodingException e){
e.printStackTrace();
return null;
}
}
//微信扫码后回调的方法
@GetMapping("callback")
public String callback(String code,String state){
//1.获取临时票据
System.out.println("code:" + code);
//2.根据code和微信id和密钥,请求微信固定地址,得到两个值
//使用code和appid以及appscrect换取access_token
StringBuffer baseAccessTokenUrl = new StringBuffer()
.append("https://api.weixin.qq.com/sns/oauth2/access_token")
append("?appid=%s")
.append("&secret=%s")
.append("&code=%s")
.append("&grant_type=authorization_code");
//对以上字符串占位符设置值
String accessTokenUrl = String.format(baseAccessTokenUrl.toString(),
ConstentPropertiesUtil.WX_OPEN_APP_ID,
ConstantPropertiesUtil.WX_OPEN_APP_SECRET,
code);
try{
//使用httpclient工具类请求这个地址,返回信息
String accesstokenInfo = HttpClientUtils.get(accesTokenUrl);
//从返回的字符串获取两个值openid和access_token(通过json工具先转换成json对象)
JSONObject jsonObject = JSONObject.parseObject(accesstokenInfo);
String access_token = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
//拿着openid和access_token请求微信地址,得到扫码人的信息
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/serinfo"+"?access_token=%s" + "&openid=%s";
String userInfoUrl = String.format(baseUserInfoUrl,accessToken,openId);
JSONObject resultUserInfoJson = JSONObject.parseObject(resultInfo);
String nickname = resultUserInfoJson.getString("nickname");
String headimgurl = resultUserInfoJson.getString("headimgurl");
//将获取到的扫描人信息添加数据库
UserInfo userInfo = new UserInfo();
userInfo.setNickName(nickname);
userInfo.etOpenid(openid);
userInfo.setStatus(1);
userInfoService.save(userInfo);
}catch(Exception e){
e.printStackTrace();
}
return "redirect"+;//跳转到页面
}
}
整合前端页面,显示二维码
/api/weixin.js
import request from '@/utils/request'
const api_name = '/api/ucenter/wx'
export default{
etLoginParam(){
return request({
url:'${api_name}/getLoginParam',
method:'get'
})
}
}
myheader.vue
<script>
import weixinApi from '@/api/weixin'
//在mounted方法中(页面渲染之后执行)
mounted(){
//初始化微信js(在body下面加上文件)
const script = document.createElement('script');
script.type = 'text/javascript'
script.src = 'https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js'
document.body.appendChild(script)
}
//methods中的微信相关参数
weixinLogin(){
this.dialogAtrr.showLoginType = 'weixin'
//微信官方
weixinApi.getLoginParam().then(response =>{
var obj = new WxLogin({
self_redirect:true,
id:'weixinLogin',//需要显示的容器id
appid:response.data.appid, //公众号appid wx****
scope:response.data.scope, //网页默认即可
redirect_uri:response.data.redirectUri,//授权成功后回调的url
state:response.data.state, //可设置为简单的随机数加session用来校验
style:'black',//提供'black','white'可选,二维码的样式
href:'' //外部css文件url,需要https
})
})
}
</script>


采用httpclient模拟浏览器的请求并返回数据
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
public class HttpClientUtils{
public static final int connTimeout = 1000;
public static final int readTimeout = 1000;
public static final String charset = "UTF-8";
private static HttpCient client = null;
static{
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(128);
cm.setDefaultMaxPerRoute(128);
client = HttpClients.custom().setConnectionManager(cm).build();
}
public static String postParamenters(String url,String parameterStr){
return post(url,parameterStr,"application/x-www-form-urlencoded",);
}
public static String postParameters(String url,String parameterStr,String charset){
return post(url,parameterStr,"application/x-www-form-urlencoded",);
}
public static String postParameters(String url,Map<String,String> params){
return postForm(url,params,null,connTimeout,readTimeout)
}
}
mounted(){
//通过请求路径获取问号后面的参数
let token = this.$route.query.token
let name = this.$route.query.name
let openid = this.$route.query.openid
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)