博客
关于我
RuoYi-Vue微信小程序登录授权
阅读量:603 次
发布时间:2019-03-11

本文共 8016 字,大约阅读时间需要 26 分钟。

微信小程序登录授权

需求分析

微信小程序登录通常通过在后台上传一个 code,后台利用 code 调用微信端接口获取 openIdsessionKey。在某些场景下,sessionKey 可能不需要。接着,后台将 token 返回给小程序,后续请求中小程序需携带 token,后台需解析验证 token

值得注意的是,小程序通常没有用户信息表,这与后台的用户表并不完全一致。


解决方案

为简化代码改动,可以通过 openIdsessionKey 伪造一个用户名密码。这样可以在不修改用户表的情况下,直接在 LoginUser 类中添加 WxUser 属性。在 SysLoginService 类中扩展一个微信登录方法,该方法仅进行假授权,避免调用 UserDetailsServiceImpl.loadUserByUsername。这样可以实现对 token 的统一管理和验证。


代码改造

新建 WxUser

common 模块下新建 WxUser 类,与 SysUser 同级。

package com.ruoyi.common.core.domain.entity;public class WxUser extends BaseEntity {    private static final long serialVersionUID = 1L;    private String openId;    private String sessionKey;    public WxUser(String openId, String sessionKey) {        super();        this.openId = openId;        this.sessionKey = sessionKey;    }    public String getOpenId() {        return openId;    }    public void setOpenId(String openId) {        this.openId = openId;    }    public String getSessionKey() {        return sessionKey;    }    public void setSessionKey(String sessionKey) {        this.sessionKey = sessionKey;    }    @Override    public String toString() {        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)                .append("openId", getOpenId())                .append("sessionKey", getSessionKey())                .toString();    }}
修改 LoginUser

LoginUser 类中加入 WxUser 属性,并生成 gettersetter。同时重写 getPasswordgetUsername 方法。

package com.ruoyi.common.core.domain.model;import java.util.Collection;import java.util.Set;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import com.fasterxml.jackson.annotation.JsonIgnore;import com.ruoyi.common.core.domain.entity.SysUser;import com.ruoyi.common.core.domain.entity.WxUser;public class LoginUser implements UserDetails {    private static final long serialVersionUID = 1L;    private String token;    private Long loginTime;    private Long expireTime;    private String ipaddr;    private String loginLocation;    private String browser;    private String os;    private Set
permissions; private SysUser user; private WxUser wxUser; public String getToken() { return token; } public void setToken(String token) { this.token = token; } public LoginUser() { } public LoginUser(SysUser user, Set
permissions) { this.user = user; this.permissions = permissions; } public LoginUser(WxUser wxUser) { this.wxUser = wxUser; } @JsonIgnore @Override public String getPassword() { return user != null ? user.getPassword() : wxUser.getSessionKey(); } @Override public String getUsername() { return user != null ? user.getUserName() : wxUser.getOpenId(); } @JsonIgnore @Override public boolean isAccountNonExpired() { return true; } @JsonIgnore @Override public boolean isAccountNonLocked() { return true; } @JsonIgnore @Override public boolean isCredentialsNonExpired() { return true; } @JsonIgnore @Override public boolean isEnabled() { return true; } public Long getLoginTime() { return loginTime; } public void setLoginTime(Long loginTime) { this.loginTime = loginTime; } public String getIpaddr() { return ipaddr; } public void setIpaddr(String ipaddr) { this.ipaddr = ipaddr; } public String getLoginLocation() { return loginLocation; } public void setLoginLocation(String loginLocation) { this.loginLocation = loginLocation; } public String getBrowser() { return browser; } public void setBrowser(String browser) { this.browser = browser; } public String getOs() { return os; } public void setOs(String os) { this.os = os; } public Long getExpireTime() { return expireTime; } public void setExpireTime(Long expireTime) { this.expireTime = expireTime; } public Set
getPermissions() { return permissions; } public void setPermissions(Set
permissions) { this.permissions = permissions; } public SysUser getUser() { return user; } public void setUser(SysUser user) { this.user = user; } public WxUser getWxUser() { return wxUser; } public void setWxUser(WxUser wxUser) { this.wxUser = wxUser; } @Override public Collection
getAuthorities() { return null; }}
修改 SysLoginService

SysLoginService 类中增加微信登录处理方法,进行假授权操作。

package com.ruoyi.sys.login.service;import com.ruoyi.common.core.domain.model.LoginUser;import com.ruoyi.security.service.SecurityContextHolder;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.stereotype.service;@Servicepublic class SysLoginService {    private static final String Wx_PREFIX = "WX_";    public String wxLogin(String openId, String sessionKey) {        LoginUser loginUser = new LoginUser(new WxUser(openId, sessionKey));        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(                openId, sessionKey, loginUser        );        SecurityContextHolder.getContext().setAuthentication(authenticationToken);        return tokenService.createToken(loginUser);    }}
修改 SysLoginController

SysLoginController 类中增加微信登录接口。

package com.ruoyi.sys.login.controller;import com.ruoyi.common.constant.Constants;import com.ruoyi.security.service.SecurityContextHolder;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.stereotype.controller;import java.util.HashMap;import java.util.Map;@Controllerpublic class SysLoginController {    private static final String LOGIN_PATH = "/api/wx/login";    @PostMapping(LOGIN_PATH)    public ResponseEntity
login(String appId, String code) { String _prefix = "【小程序登录】"; if (StringUtils.isBlank(appId)) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST, null, _prefix + "appId为空"); } if (StringUtils.isBlank(code)) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST, null, _prefix + "登录凭证不能为空"); } WxConfig config = new WxConfigImpl(appId); WxaLogin wxaLogin = new WxaLogin(config); String result = wxaLogin.code2Session(code); _log.info("{}微信根据code获取用户信息结果:{}", _prefix, result); JSONObject code2SessionRes = JSON.parseObject(result); String openId = code2SessionRes.getString("openid"); String sessionKey = code2SessionRes.getString("session_key"); if (StringUtils.isBlank(openId)) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST, null, _prefix + "登录失败,无效的登录凭证"); } Map
response = new HashMap<>(); response.put(Constants.TOKEN, loginService.wxLogin(openId, sessionKey)); _log.info("{}生成的token:{}", _prefix, response.get(Constants.TOKEN)); return new ResponseEntity<>(response, HttpStatus.OK); }}
修改 SecurityConfig

SecurityConfig 类中放行登录接口。

package com.ruoyi.security.config;import org.springframework.security.config.annotation.builders.WebSecurity;import org.springframework.security.config.annotation.web.builders.HttpInterceptor;import org.springframework.security.config.annotation.web.config.WebMvcConfigurer;@Configurationpublic class SecurityConfig extends WebMvcConfigurer {    @Override    public void addInterceptors(WebSecurity webSecurity) {        webSecurity            .antMatchers("/login", "/captchaImage", "/api/wx/login")            .anonymous()        ;    }}

总结

通过以上改造,可以实现微信小程序的登录授权。小程序登录时,后台接收 code,调用微信接口获取 openIdsessionKey,然后调用 wxLogin 方法生成 token。小程序后续请求需携带 token,后台通过 LoginUser 类验证 token。这一方案无需修改用户表,且通过伪造用户名密码的方式实现了统一管理,保证了系统安全性。

转载地址:http://nkatz.baihongyu.com/

你可能感兴趣的文章
Objective-C实现是否为 Pythagoreantriplet 毕氏三元数组算法(附完整源码)
查看>>
Objective-C实现显示响应算法(附完整源码)
查看>>
Objective-C实现普通矩阵A和B的乘积(附完整源码)
查看>>
Objective-C实现更新数字指定偏移量上的值updateBit算法(附完整源码)
查看>>
Objective-C实现最大类间方差法OTSU算法(附完整源码)
查看>>
Objective-C实现最大非相邻和算法(附完整源码)
查看>>
Objective-C实现最小二乘多项式曲线拟合(附完整源码)
查看>>
Objective-C实现最小路径和算法(附完整源码)
查看>>
Objective-C实现最快的归并排序算法(附完整源码)
查看>>
Objective-C实现最长公共子序列算法(附完整源码)
查看>>
Objective-C实现最长回文子串算法(附完整源码)
查看>>
Objective-C实现最长回文子序列算法(附完整源码)
查看>>
Objective-C实现最长子数组算法(附完整源码)
查看>>
Objective-C实现最长字符串链(附完整源码)
查看>>
Objective-C实现最长递增子序列算法(附完整源码)
查看>>
Objective-C实现有限状态机(附完整源码)
查看>>
Objective-C实现有限状态自动机FSM(附完整源码)
查看>>
Objective-C实现有限集上给定关系的自反关系矩阵和对称闭包关系矩阵(附完整源码)
查看>>
Objective-C实现朴素贝叶斯算法(附完整源码)
查看>>
Objective-C实现杰卡德距离算法(附完整源码)
查看>>