开始时间:2022-06-17
课程链接:尚医通项目
开放系统间授权
照片拥有者想要在云冲印服务上打印照片,云冲印服务需要访问云存储服务上的资源
方式一:用户名密码复制:将受保护的资源中的用户名和密码存储在客户应用的服务器上,使用时直接使用这个用户名和密码登录适用于同一公司内部的多个系统,不适用于不受信的第三方应用
方式二:通用开发者key,适用于合作商或者授信的不同业务部门之间,但如果公司规模体量不对等,也不会去开通这项业务
==方式三:颁发令牌,==可以设置令牌有效时间,在这段时间允许访问,可以随时收回
单点登录
现代微服务中系统微服务化以及应用的形态和设备类型增多,不能用传统的登录方式
核心的技术不是用户名和密码,而是token,由AuthServer颁发token,用户使用token进行登录
微信登录前期准备有些麻烦,参考文档
这里老师直接给了他的一个用户
操作模块:service-user
我们的操作步骤为:
在application-dev.yml添加配置
# 服务端口
server.port=8160
wx.open.app_id=wxed9954c01bb89b47
wx.open.app_secret=a7482517235173ddb4083788de60b90e
wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
yygh.baseUrl=http://localhost:3000
看看效果
正常微信扫码的样子
调用流程
添加配置
service_user下
wx.open.app_id=wxed9954c01bb89b47
wx.open.app_secret=a7482517235173ddb4083788de60b90e
wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
yygh.baseUrl=http://localhost:3000
添加配置类
package com.bupt.yygh.user.utils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class ConstantWxPropertiesUtils implements InitializingBean {
@Value("${wx.open.app_id}")
private String appId;
@Value("${wx.open.app_secret}")
private String appSecret;
@Value("${wx.open.redirect_url}")
private String redirectUrl;
@Value("${yygh.baseUrl}")
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 afterPropertiesSet() throws Exception {
WX_OPEN_APP_ID = appId;
WX_OPEN_APP_SECRET = appSecret;
WX_OPEN_REDIRECT_URL = redirectUrl;
YYGH_BASE_URL = yyghBaseUrl;
}
}
添加接口
package com.bupt.yygh.user.api;
import com.alibaba.fastjson.JSONObject;
import com.bupt.yygh.common.helper.JwtHelper;
import com.bupt.yygh.common.result.Result;
import com.bupt.yygh.model.user.UserInfo;
import com.bupt.yygh.user.service.UserInfoService;
import com.bupt.yygh.user.utils.ConstantWxPropertiesUtils;
import com.bupt.yygh.user.utils.HttpClientUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
@Api(tags = "微信扫码登录服务")
//微信操作的接口
@Controller
//用Controller为了跳转方便
// 但是这里不是RestController,所以我们下面要自己补充ResponseBody
@RequestMapping("/api/ucenter/wx")
public class WeixinApiController {
@Autowired
private UserInfoService userInfoService;
//1 生成微信扫描二维码
//返回生成二维码需要参数
@GetMapping("getLoginParam")
@ResponseBody
@ApiOperation("生成微信扫码二维码")
public Result genQrConnect() {
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", 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) {
//第一步 获取临时票据 code
System.out.println("code:" + code);
//第二步 拿着code和微信id和秘钥,请求微信固定地址 ,得到两个值
//使用code和appid以及appscrect换取access_token
// %s 占位符
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(),
ConstantWxPropertiesUtils.WX_OPEN_APP_ID,
ConstantWxPropertiesUtils.WX_OPEN_APP_SECRET,
code);
//使用httpclient请求这个地址
try {
String accesstokenInfo = HttpClientUtils.get(accessTokenUrl);
System.out.println("accesstokenInfo:" + accesstokenInfo);
//从返回字符串获取两个值 openid 和 access_token
JSONObject jsonObject = JSONObject.parseObject(accesstokenInfo);
String access_token = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
//判断数据库是否存在微信的扫描人信息
//根据openid判断
UserInfo userInfo = userInfoService.selectWxInfoOpenId(openid);
if (userInfo == null) { //数据库不存在微信信息
// 第三步 拿着openid 和 access_token请求微信地址,得到扫描人信息
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
String userInfoUrl = String.format(baseUserInfoUrl, access_token, openid);
String resultInfo = HttpClientUtils.get(userInfoUrl);
System.out.println("resultInfo:" + resultInfo);
JSONObject resultUserInfoJson = JSONObject.parseObject(resultInfo);
//解析用户信息
//用户昵称
String nickname = resultUserInfoJson.getString("nickname");
//用户头像
String headimgurl = resultUserInfoJson.getString("headimgurl");
//获取扫描人信息添加数据库
userInfo = new UserInfo();
userInfo.setNickName(nickname);
userInfo.setOpenid(openid);
userInfo.setStatus(1);
userInfoService.save(userInfo);
}
//返回name和token字符串
Map<String, String> map = new HashMap<>();
String name = userInfo.getName();
if (StringUtils.isEmpty(name)) {
name = userInfo.getNickName();
}
if (StringUtils.isEmpty(name)) {
name = userInfo.getEmail();
}
map.put("name", name);
//判断userInfo是否有邮箱号,如果邮箱号为空,返回openid
//如果邮箱号不为空,返回openid值是空字符串
//前端判断:如果openid不为空,绑定邮箱号,如果openid为空,不需要绑定邮箱号
if (StringUtils.isEmpty(userInfo.getEmail())) {
map.put("openid", userInfo.getOpenid());
} else {
map.put("openid", "");
}
//使用jwt生成token字符串
String token = JwtHelper.createToken(userInfo.getId(), name);
map.put("token", token);
//跳转到前端页面
return "redirect:" + ConstantWxPropertiesUtils.YYGH_BASE_URL + "/weixin/callback?token=" + map.get("token") + "&openid=" + map.get("openid") +
"&name=" + URLEncoder.encode(map.get("name"), "utf-8");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
注意:
//微服务的service-user的端口号
★ 第一 修改项目启动端口号为 8160
★ 第二步 把回调地址改为 wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
具体完整代码参考Gitee第十五次更新
我测试swagger以及看我的网关服务,都是没问题的
然后我发现是因为网关的配置问题
为了完成刚刚微信扫码的功能,添加了一个过滤器,把过滤器注释掉后就能用了,我也就不改过滤器本身的配置了
用户认证需要上传证件图片、首页轮播也需要上传图片,因此我们要做文件服务,阿里云oss是一个很好的分布式文件服务系统,所以我们只需要集成阿里云oss即可
在阿里云里面创建OSS
在文件管理中上传文件
通过url路径访问地址
获取对应的accessKey
通过代码创建bucket,注意不能跟已有bucket同名,否则会报错
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.CreateBucketRequest;
public class test {
public static void main(String[] args) {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = "LTAI5tNKutqsvWPSXAuSg679";
String accessKeySecret = "vwynb14tvmrJOJFsE4MKH8z6Tk6FNS";
// 填写Bucket名称,例如examplebucket。
String bucketName = "yygh-test-oss-jdh";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
// 创建CreateBucketRequest对象。
CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
// 如果创建存储空间的同时需要指定存储类型和数据容灾类型, 请参考如下代码。
// 此处以设置存储空间的存储类型为标准存储为例介绍。
//createBucketRequest.setStorageClass(StorageClass.Standard);
// 数据容灾类型默认为本地冗余存储,即DataRedundancyType.LRS。如果需要设置数据容灾类型为同城冗余存储,请设置为DataRedundancyType.ZRS。
//createBucketRequest.setDataRedundancyType(DataRedundancyType.ZRS);
// 设置存储空间的权限为公共读,默认为私有。
//createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
// 创建存储空间。
ossClient.createBucket(createBucketRequest);
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}
生成成功
在service_user中的UserInfoAPIController写上
//用户认证接口
@PostMapping("auth/userAuth")
public Result userAuth(@RequestBody UserAuthVo userAuthVo, HttpServletRequest request) {
//传递两个参数,第一个参数用户id,第二个参数认证数据vo对象
userInfoService.userAuth(AuthContextHolder.getUserId(request),userAuthVo);
return Result.ok();
}
//获取用户id信息接口
@GetMapping("auth/getUserInfo")
public Result getUserInfo(HttpServletRequest request) {
Long userId = AuthContextHolder.getUserId(request);
UserInfo userInfo = userInfoService.getById(userId);
return Result.ok(userInfo);
}
对应接口实现类
//用户认证
@Override
public void userAuth(Long userId, UserAuthVo userAuthVo) {
//根据用户id查询用户信息
UserInfo userInfo = baseMapper.selectById(userId);
//设置认证信息
//认证人姓名
userInfo.setName(userAuthVo.getName());
//其他认证信息
userInfo.setCertificatesType(userAuthVo.getCertificatesType());
userInfo.setCertificatesNo(userAuthVo.getCertificatesNo());
userInfo.setCertificatesUrl(userAuthVo.getCertificatesUrl());
userInfo.setAuthStatus(AuthStatusEnum.AUTH_RUN.getStatus());
//进行信息更新
baseMapper.updateById(userInfo);
}
前端页面copy过来的,看下面效果
可以查看数据库的yygh_user里面,将certificate也填上了
因为认证了的账号可以给亲属挂号
看看效果
在user_info表中
在patient表中
注意patient的user_id要和user_info里面对应
就可以查看到结果
点击查看详情
修改
添加
具体代码还是得看码云对应的第十八次更新
这一块是在9528页面上操作的,不能搞错了
如果启动自己的微服务显示端口被占用,不要慌,关掉idea重启一下
先在列表里找到,只有auth_status为1的数据才能显示
点击通过后可以看到数据库里的值得到了更新
整个平台用户管理的代码参考码云的更新:第十九次更新,完成了平台管理功能:用户列表功能、用户锁定功能、用户详情功能、用户认证审批
结束时间:2022-06-25