文章目录
一、注册新用户的业务要点说明
管理者在创建新员工记录的时候,并不知道新员工的微信账号,所以没办法完成绑定,需要新员工自己完成绑定。于是新员工在Emos小程序注册页面,填写激活码之后,Emos小程序获取新员工的微信账号信息,提交给Emos后台系统,后端系统把微信账号与新员工账号关联在一起,然后保存到MySQL数据库。
其实把微信账号与小程序关联在一起很简单,就是把微信基本信息(昵称、头像)和 OpenId ,存储到员工记录上面。因为每个微信账号的 OpenId 在当前的小程序上面都是 唯一的 ,只要MySQL记录下每名员工微信账号的 OpenId ,那么就实现了员工记录与微信账号的绑定。员工登陆Emos小程序的时候,后端系统会拿着这个微信帐户的 OpenId 与数据库中的 OpenId 比对。如果用户表不存在这个 OpenId ,说明该微信帐户没有绑定员工账号,所以Emos拒绝用户登陆。如果用户表存在这个 OpenId ,那么就判定用户登陆成功。
二、如何获取微信账号的OpenId字符串
首先可以从微信APP上获取用户临时授权字符串(Code),然后把这个字符串传递给后端java系统,让系统拿着AppId,密匙,和Code,提交给微信平台换取OpenId
String url = "https://api.weixin.qq.com/sns/jscode2session"
HashMap map = new HashMap();
map.put("appid",appId);
map.put("secret",appSecret);
map.put("js_code",code);
map.put("grant_type","authorization_code");
String response = HttpUtil.post(url.map);
JSONObject json = JSONUtil.parseObj(response);
String openId = json.getSrt("openid");
1.获取临时授权和微信基本信息
uni.login({
provider: 'weixin',
success: function(resp){
console.log(resp.code);
}
})
uni.login({
provider: 'weixin',
success: function(resp){
let code = resp.code;
uni.getUserInfo({
provider: 'weixin',
success: function(resp){
let nickname = reap.userInfo.nickname;
let avatarUrl = resp.userInfo.avataUrl;
}
})
}
})
三、实现注册超级管理员功能(持久层)
1.判断系统是否已经绑定超级管理员
Emos系统中只可以绑定唯一的超级管理员账号,当用户输入000000这个激活码时,后端必须要判断是否可以绑定超级管理员,通过SQL语句查询用户表是否存在超级管理员账户,只要查询root字段值为1的记录数量就可以
<select id="haveRootUser" resultType="boolean">
SELECT if(COUNT(*),TRUE,FALSE) FROM tb_user WHERE root=1;
</select>
2.编写保存用户记录的代码
假设业务层判断用户可以注册为超级管理员,把用户数据保存在用户表
<insert id="insert" parameterType="HashMap">
INSERT INTO tb_user
SET
<if test="openId!=null">
open_id = #{openId},
</if>
<if test="nickname!=null">
nickname = #{nickname},
</if>
<if test="photo!=null">
photo = #{photo},
</if>
<if test="name!=null">
name = #{name},
</if>
<if test="sex!=null">
sex = #{sex},
</if>
<if test="tel!=null">
tel = #{tel},
</if>
<if test="email!=null">
email=#{email},
</if>
<if test="hiredate!=null">
hiredate = #{hiredate},
</if>
role = #{role},
root = #{root},
<if test="deptName!=null">
dept_id = ( SELECT id FROM tb_dept WHERE dept_name = #{deptName} ),
</if>
status = #{status},
create_time = #{createTime}
</insert>
3.编写查询用户ID代码
如果在员工表中插入新纪录,由于主键是自动生成的,所以我们并不知道新纪录的主键值是多少。于是我们要编写代码,根据OpenId查询用户ID
<select id="searchIdByOpenId" resultType="Integer" parameterType="String">
SELCET id FROM tb_user WHERE open_id = #{openId} AND status=1;
<select>
四、实现注册超级管理员功能(业务层)
1.获取OpenId
获取微信用户的OpenId,需要后端程序向微信平台发出请求,并上传若干参数,最终才能得到。
@Service
@Slf4j
@Scope("prototype")
public class UserServiceImpl implements UserService{
@Value("${wx.app-id}")
private String appId;
@Value("${wx.app-secret}")
private String appSecret;
@Autowired
private TbUserDao userDao;
private String getOpenId(String code){
String url = "https://api.weixin.qq.com/sns/jscode2session";
HashMap map = new HashMap();
map.put("appId",appId);
map.put("secret",appSecret);
map.put("js_code",code);
map.put("grant_type","authorization_code");
String response = HttpUtil.post(url,map);
JSONObject json = JSONUtil.parseObj(response);
String openId = json.getStr("openid");
if(openId == null || openId.length() == 0){
throw new RuntimeException("临时登录凭证错误");
}
return openId;
}
}
2.注册新用户
@Override
public int registerUser(String regisetrCode,String code,String nickname,String photo){
if(registerCode.equals("000000")){
boolean bool = userDao.haveRootUser();
if(!bool){
String openId = getOpenId(code);
HashMap param = new HashMap();
param.put("openId",openId);
param.put("nickname",nickname);
param.put("photo",photo);
param.put("role","[0]");
param.put("status",1);
param.put("createTime",new Date());
param.put("root",true);
userDao.insert(param);
int id = userDao.searchIdByOpenId(openId);
return id;
}else{
throw new EmosException("无法绑定超级管理员账户");
}
}
//此处还有其他判断
else{
return 0;
}
}
五、掌握RABC权限模型
1.RABC权限模型
RBAC的基本思想是对系统操作的权限不是直接授予具体的用户,而是在用户集合和权限集合间建立一个角,每一种角色对应一种相应的权限,这样做的好处在于不用在每次创建用户时都进行分配权限的操作,只要分配相应的角色即可,而角色的权限变更比用户的权限变更要少得多,这样将简化用户得权限管理。
在角色表(tb_role)中设置得permissions字段,类型是JSON格式的
在用户表(tb_user)上设置role字段,类型也为JSON的
2.前后端权限验证
关于权限验证的工作,前端要做,后端也要做。后端的权限验证还好说,Shiro框架可以做这个事情。但是移动端没有权限验证框架,所以需要我们自己封装函数来验证权限。每个页面在渲染的时候,先判断用户拥有什么权限,然后根据权限控制渲染的内容。比如说普通员工没有添加新员工的权限,所以界面上就不能出现添加按钮。
移动端做权限判断的前提是必须有当前用户的 权限列表 ,这个权限列表是用户 登陆成功 或者 注册成功 ,后端Java项目返回给移动端的,移动端保存到本地 Storage 里面。
3.如何查询用户的权限列表?
<select id="searchUserPermission" parameterType="int" resultType="String">
SELECT p.permission_name
FROM tb_user u
JOIN tb_role ON JSON_CONTAINS(u.role,CAST(r.id AS CHAR))
JOIN tb_permission p ON JSON_CONTAINS(r.permission,CAST(p.id AS CHAR))
WHERE u.id = #{userId} AND u.status = 1;
</select>
六、实现注册超级管理员功能(Web层)
接收移动端提交的注册请求,我们需要用表单类来封装数据,所以创建 RegisterForm.java 类。
1.创建表单
@Date
@ApiModel
public class RegisterForm{
@NotBlank(message = "注册码不能为空")
@Pattern(regexp = "^[0-9]{6}$",message = "注册码必须是6位数字")
private String registerCode;
@NotBlank(message = "微信临时授权不能为空")
private String code;
@NotBlank(message = "呢称不能为空")
private String nickname;
@NotBlank(message = "头像不能为空")
private String photo;
}
2.创建Controller类
@RestController
@RequestMapping("/user")
@Api("用户模块Web接口")
public class UserController{
@Autowired
private UserService userService;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private RedisTemplate redisTemplate;
@Value("${emos.jwt.cache-expire}")
private int cacheExpire;
@PostMapping("/register")
@ApiModel("注册用户")
public R register(@Valid @RequestBody RegisterForm form){
int id = userService.registerUser(form.getRegisterCode(),form.getCode(),form. getNickname(),form.getPhoto);
String token = jwtUtil.createToken(id);
Set<String> permsSet = userService.searchUserPermission(id);
saveCacheToken(token,id);
return R.ok("用户注册成功").put("token",token).put("permsSet",permsSet);
}
public void saveCacheToken(String token, int userId){
redisTemplate.opsForValue().set(token,userId+"",cacheExpire,TimeUnit.DAYS);
}
}
六、定义全局路径和封装Ajax(移动端)
移动端发出请求,首先要填写好URL地址。为了在移动端项目上集中管理URL路径,我们可以在 main.js 文件中用全局变量的语法,定义全局的URL地址,这样更加便于维护。
let baseUrl = "http://192.168.99.216:8080/emos-wx-api"
Vue.prototype.url={
register: baseUrl+"/user/register",
}
移动端通过Ajax向服务端提交请求,然后接收到的响应分若干种情况:如果移动端每次发出Ajax,都要做这么多的判断,我们的重复性劳动太多了。所以尽可能的把
Ajax封装起来,减少重复性的劳动
Vue.prototype.ajax=function(url,methon,data,fun){
uni.request({
"url":url,
"method":method,
"header":{
token:uni.getStorageSync('token')
},
"data":data,
success: function(resp){
if(resp.statusCode == 401){
uni.redirecTo({
url:'../login/login'
});
}else if(resp.statusCode == 200 && resp.data.code == 200){
let data == resp.data
if(data.hasOwnProperty("token")){
let token = data.token
uni.setStorageSync("token",token)
}
fun(resp)
}else{
uni.showToast({
icon:'none',
title:reps.data
});
}
}
});
}
1.前端验证功能
let that=this;
if(that.registerCode == null || that.registerCode.length == 0){
uni.showToast({
icon:'none',
title:'邀请码不能为空'
});
return
}
else if(/^[0-9]{6}$/.test(that.registerCode) == false) {
uni.showToast({
icon:'none',
title:'邀请码必须为6位数字'
});
}
2.提交Ajax请求
let data={
code:code,
nickname:nickname,
photo:photo,
registerCode:that.registerCode
}
that.ajax(that.url.register,'POST',data,function(resp)){
let permission = resp.data.permission;
uni.setStorageSync('permission',permission);
//跳转到index页面
}
七、实现用户登录功能
1.如何判定登录
用户表(tb_user)中没有密码字段,无法根据username和password判断用户是否可以登录。在EMOS系统中用户拿着微信登录小程序,在用户表中只有openId,nickname和photo跟微信账号相关。
用户在EMOS系统登录,然后小程序把临时字符串给后端系统,后端拿着临时字符串换取openid,然后查询用户表中是否存在openid,如果存在,表示用户已经注册,可以登录
Dao
<select id="searchIdByOpenId" parameterType="String" resultType="Integer">
SELECT id FROM tb_user WHERE open_id=#{openId} AND status = 1
<select>
Service
public Integer login(String code){
String openId = getOpenId(code);
Integer id = userDao.searchIdByOpenId(openId);
if(id == null){
throw new EmosException("账户不存在");
}
//从消息队列中接受消息,转移到消息表
return id;
}
Web
封装客户提交的数据
@ApiModel
@Data
public class LoginForm{
@NotBlank(message = "临时授权不能为空")
private String code;
}
@PostMapping("/login")
@ApiOperation("登录系统")
public R login(@Valid @RequestBody LoginForm form){
int id = userService.login(form.getCode());
String token = jwtUtil.createToken(id);
Set<String> permission = userService.searchUserPermission(id);
saveCacheToken(token,id);
return R.ok("登录成功").put("token",token).put("permission",permission);
}
判断用户登录成功后,向客户端返回权限列表和token令牌
2.移动端
Vue.prototype.url={
login:baseUrl+"/user/login"
}
let that = this;
uni.login({
provider:'weixin',
success:function(resp){
let code = resp.code;
that.ajax(that.url.login,'POST',{"code":code},function(resp)){
let permission = resp.data.permission;
uni.setStroageSync('permission',permission);
//跳转到登录页面
})
},
fail:function(e){
console.log(e)
uni.showToast({
icon:"none",
title:"执行异常"
})
}
})