java微信小程序登录查MySQL_微信小程序登录JAVA后台

本文介绍了使用Java SpringBoot后台实现微信小程序登录的过程,包括项目结构、配置、依赖管理、登录流程、权限配置以及相关实体类和接口。通过微信官方API获取登录凭证,结合Redis存储会话信息,实现用户身份验证。
摘要由CSDN通过智能技术生成

登录流程时序登录流程时序

L3Byb3h5L2h0dHBzL21wLndlaXhpbi5xcS5jb20vZGVidWcvd3hhZG9jL2Rldi9pbWFnZS9hcGktbG9naW4uanBnP3Q9MjAxODMxNQ==.jpg

具体的登录说明查看 小程序官方API

项目的结构图:

8815c85486eb5aedfed96165d5b5e362.png

springboot项目搭建

使用idea作为开发工具,由gradle构建项目,搭建springboot项目,对这块儿不熟悉的可以自行去学习,此处不多赘述。下面是核心的配置文件。application.yml中配置springboot默认的参数,application.properties配置自定义的参数,可以统一配置在一个文件中,依据个人习惯。

buidle.gradle配置

buildscript {

ext {

springBootVersion = '1.5.10.RELEASE'

}

repositories {

mavenLocal()

maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }

mavenCentral()

}

dependencies {

classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")

}

}

apply plugin: 'java'

apply plugin: 'org.springframework.boot'

group = 'xin.yangmj'

version = '1.0.1'

sourceCompatibility = 1.8

repositories {

mavenLocal()

maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }

mavenCentral()

}

dependencies {

compile('org.springframework.boot:spring-boot-starter-cache')

compile('org.springframework.boot:spring-boot-starter-data-redis')

compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.1')

compile('org.springframework.boot:spring-boot-starter-security')

compile('org.springframework.boot:spring-boot-starter-web')

compile('mysql:mysql-connector-java')

compile('org.springframework.security:spring-security-test')

testCompile('org.springframework.boot:spring-boot-starter-test')

compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'

}

application.yml

logging:

level:

root: DEBUG

spring:

datasource:

url: jdbc:mysql://localhost/remindme?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8

username: root

password: root

driver-class-name: com.mysql.jdbc.Driver

redis:

host: localhost

password:

port: 6379

mybatis:

mapperLocations: classpath:mapper/*.xml

configuration:

mapUnderscoreToCamelCase: true

default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler

application.properties

# JWT相关配置

jwt.header=Authorization

# 过期时间

jwt.expiration=864000

# 注意有一个空格

jwt.tokenHead=Bearer

# wechat Auth

auth.wechat.sessionHost=https://api.weixin.qq.com/sns/jscode2session

auth.wechat.appId=***

auth.wechat.secret=***

auth.wechat.grantType=authorization_code

权限相关的配置

WebSecurityConfig.java

@Configuration

@EnableWebSecurity

@EnableGlobalMethodSecurity(prePostEnabled = true)

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

private JwtAuthenticationEntryPoint unauthorizedHandler;

@Bean

public ThirdSessionAuthFilter authenticationTokenFilterBean() throws Exception {

return new ThirdSessionAuthFilter();

}

@Override

protected void configure(HttpSecurity httpSecurity) throws Exception {

httpSecurity

// 由于使用的是JWT,我们这里不需要csrf

.csrf().disable()

.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()

// 基于token,所以不需要session

.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

.authorizeRequests()

// 允许对test的无授权访问

.antMatchers(HttpMethod.GET, "/test").permitAll()

// 对于获取token的rest api要允许匿名访问

.antMatchers("/auth").permitAll();

// 添加本地地三方session filter

httpSecurity

.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);

// 禁用缓存

httpSecurity.headers().cacheControl();

}

}

ThirdSessionAuthFilter.java

@Component

public class ThirdSessionAuthFilter extends OncePerRequestFilter {

@Value("${jwt.header}")

private String tokenHeader;

@Value("${jwt.tokenHead}")

private String tokenHead;

@Autowired

private StringRedisTemplate stringRedisTemplate;

@Autowired

private ConsumerMapper consumerMapper;

@Override

protected void doFilterInternal(HttpServletRequest request,

HttpServletResponse response,

FilterChain chain) throws ServletException, IOException {

//获取请求头部分的Authorization

String authHeader = request.getHeader(this.tokenHeader);

//如果请求路径为微信通知后台支付结果则不需要token(之后会在具体的controller中,对双方签名进行验证防钓鱼)

String url = request.getRequestURI().substring(request.getContextPath().length());

if (url.equals("/auth") || url.equals("/test")) {

chain.doFilter(request, response);

return;

}

if (null == authHeader || !authHeader.startsWith("Bearer")) {

throw new RuntimeException("非法访问用户");

}

// The part after "Bearer "

final String thirdSessionId = authHeader.substring(tokenHead.length());

String wxSessionObj = stringRedisTemplate.opsForValue().get(thirdSessionId);

if (StringUtils.isEmpty(wxSessionObj)) {

throw new RuntimeException("用户身份已过期");

}

// 设置当前登录用户

try (AppContext appContext = new AppContext(wxSessionObj.substring(wxSessionObj.indexOf("#") + 1))) {

chain.doFilter(request, response);

}

}

}

AppContext.java

public class AppContext implements AutoCloseable {

private static final ThreadLocal CURRENT_CONSUMER_WECHAT_OPENID = new ThreadLocal<>();

public AppContext(String wechatOpenid) {

CURRENT_CONSUMER_WECHAT_OPENID.set(wechatOpenid);

}

@Override

public void close() {

CURRENT_CONSUMER_WECHAT_OPENID.remove();

}

public static String getCurrentUserWechatOpenId() {

return CURRENT_CONSUMER_WECHAT_OPENID.get();

}

}

JwtAuthenticationEntryPoint.java

@Component

public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {

private static final long serialVersionUID = -8970718410437077606L;

@Override

public void commence(HttpServletRequest request,

HttpServletResponse response,

AuthenticationException authException) throws IOException {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");

}

}

WechatAuthProperties.java

@Component

public class WechatAuthProperties {

@Value("${auth.wechat.sessionHost}")

private String sessionHost;

@Value("${auth.wechat.appId}")

private String appId;

@Value("${auth.wechat.secret}")

private String secret;

@Value("${auth.wechat.grantType}")

private String grantType;

//省略getter setter

}

相关实体类对象

public class AccountDto {

private Long id;

private String username;

private Long phone;

private Gender gender;

private String vcode;

private String password;

private String promotionCode;

private String InvitationCode;

private String clientAssertion;

private String code;

//省略 getter setter

}

Consumer.java

public class Consumer {

private Long id;

private String username;

private String wechatOpenid;

private Long phone;

private String nickname;

private String avatarUrl;

private Gender gender;

private String email;

private Long lastLoginTime;

private Boolean deleted;

private Long createdBy;

private Long createdAt;

private Long updatedBy;

private Long updatedAt;

// 省略 gettter setter

}

Gender.java

public enum Gender {

UNKNOW(0, "未知"),

MAN(1, "先生"),

WOMAN(2, "女士");

private Byte value;

private String name;

Gender(int value, String name) {

this.value = (byte)value;

this.name = name;

}

public Byte getValue() {

return this.value;

}

public String getName() {

return this.name;

}

}

API接口类

@RestController

public class AuthEndpoint {

@Value("${jwt.header}")

private String tokenHeader;

@Value("${jwt.tokenHead}")

private String tokenHead;

@Autowired

private StringRedisTemplate stringRedisTemplate;

@Autowired

private WechatService wechatService;

@GetMapping("/test")

public String test() {

return "test_success";

}

@GetMapping("/testAuth")

public String testAuth() {

return "testAuth_success";

}

@PostMapping("/auth")

public ResponseEntity createAuthenticationToken(@RequestBody AccountDto accountDto)

throws AuthenticationException {

WechatAuthenticationResponse jwtResponse = wechatService.wechatLogin(accountDto.getCode());

return ResponseEntity.ok(jwtResponse);

}

@PostMapping("/updateConsumerInfo")

public void updateConsumerInfo(@RequestBody Consumer consumer) {

wechatService.updateConsumerInfo(consumer);

}

}

注册核心流程

@Service

public class WechatService {

private static final Logger LOGGER = LoggerFactory.getLogger(WechatService.class);

@Autowired

private ConsumerMapper consumerMapper;

/**

* 服务器第三方session有效时间,单位秒, 默认1天

*/

private static final Long EXPIRES = 86400L;

private RestTemplate wxAuthRestTemplate = new RestTemplate();

@Autowired

private WechatAuthProperties wechatAuthProperties;

@Autowired

private StringRedisTemplate stringRedisTemplate;

public WechatAuthenticationResponse wechatLogin(String code) {

WechatAuthCodeResponse response = getWxSession(code);

String wxOpenId = response.getOpenid();

String wxSessionKey = response.getSessionKey();

Consumer consumer = new Consumer();

consumer.setWechatOpenid(wxOpenId);

loginOrRegisterConsumer(consumer);

Long expires = response.getExpiresIn();

String thirdSession = create3rdSession(wxOpenId, wxSessionKey, expires);

return new WechatAuthenticationResponse(thirdSession);

}

public WechatAuthCodeResponse getWxSession(String code) {

LOGGER.info(code);

String urlString = "?appid={appid}&secret={srcret}&js_code={code}&grant_type={grantType}";

String response = wxAuthRestTemplate.getForObject(

wechatAuthProperties.getSessionHost() + urlString, String.class,

wechatAuthProperties.getAppId(),

wechatAuthProperties.getSecret(),

code,

wechatAuthProperties.getGrantType());

ObjectMapper objectMapper = new ObjectMapper();

ObjectReader reader = objectMapper.readerFor(WechatAuthCodeResponse.class);

WechatAuthCodeResponse res;

try {

res = reader.readValue(response);

} catch (IOException e) {

res = null;

LOGGER.error("反序列化失败", e);

}

LOGGER.info(response);

if (null == res) {

throw new RuntimeException("调用微信接口失败");

}

if (res.getErrcode() != null) {

throw new RuntimeException(res.getErrmsg());

}

res.setExpiresIn(res.getExpiresIn() != null ? res.getExpiresIn() : EXPIRES);

return res;

}

public String create3rdSession(String wxOpenId, String wxSessionKey, Long expires) {

String thirdSessionKey = RandomStringUtils.randomAlphanumeric(64);

StringBuffer sb = new StringBuffer();

sb.append(wxSessionKey).append("#").append(wxOpenId);

stringRedisTemplate.opsForValue().set(thirdSessionKey, sb.toString(), expires, TimeUnit.SECONDS);

return thirdSessionKey;

}

private void loginOrRegisterConsumer(Consumer consumer) {

Consumer consumer1 = consumerMapper.findConsumerByWechatOpenid(consumer.getWechatOpenid());

if (null == consumer1) {

consumerMapper.insertConsumer(consumer);

}

}

public void updateConsumerInfo(Consumer consumer) {

Consumer consumerExist = consumerMapper.findConsumerByWechatOpenid(AppContext.getCurrentUserWechatOpenId());

consumerExist.setUpdatedBy(1L);

consumerExist.setUpdatedAt(System.currentTimeMillis());

consumerExist.setGender(consumer.getGender());

consumerExist.setAvatarUrl(consumer.getAvatarUrl());

consumerExist.setWechatOpenid(consumer.getWechatOpenid());

consumerExist.setEmail(consumer.getEmail());

consumerExist.setNickname(consumer.getNickname());

consumerExist.setPhone(consumer.getPhone());

consumerExist.setUsername(consumer.getUsername());

consumerMapper.updateConsumer(consumerExist);

}

}

微信小程序代码片段

wx.login() 获取code,然后携带code发送请求到自己服务端,获取登录信息。然后 wx.getUserInfo() 获取用户的基本信息,例如:昵称、头像等,上传本地服务器保存用户基本信息。

// 登录

wx.login({

success: function(res) {

if (res.code) {

wx.request({

url: "http://localhost:8080/auth",

data: {

code: res.code

},

method: "POST",

header: {

'content-type': 'application/json',

},

success: function (res) {

console.log(res.data.access_token);

var token = res.data.access_token;

wx.getUserInfo({

success: res => {

// 保存用户信息到服务端

wx.request({

url: "http://localhost:8080/updateConsumerInfo",

data: res.userInfo,

method: "POST",

header: {

'Authorization': 'Bearer ' + token,

'content-type': 'application/json',

},

success: function (res) {

console.log("success");

},

fail: function (error) {

console.log(error);

}

})

}

})

},

fail: function (error) {

console.log(error);

}

})

} else {

console.log("error code " + res.errMsg);

}

}

})

效果展示

刷新微信小程序缓存,编译使发送请求

666bd50be50f5158d5329c275dc7ae88.png

发送登录请求,完成后获取到 access_token

390953ba0c91697f91a7cb071800dd28.png

发送获取用户信息请求

281e41be3197c238ad89070d45338ea2.png

小程序请求本地服务器登录接口

41460ce741fe07b0879b663c9ff86c34.png

本地服务器请求微信服务器登录接口

6f4c4e0e7bb716f50efd9e9076bd9a3e.png

小程序请求本地服务器更新用户信息接口

01bb370a41df4fda7f04f186239be68f.png

redis保存会话信息

49ff1b60b9af583b80ca436c154f5fac.png

mysql数据库存储用户信息

a9306d812da05ca9d09d549634b4d740.png微信小程序登录JAVA后台

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

微信小程序与Java后台通信

一.写在前面 最近接触了小程序的开发,后端选择Java,因为小程序的代码运行在腾讯的服务器上,而我们自己编写的Java代码运行在我们自己部署的服务器上,所以一开始不是很明白小程序如何与后台进行通信的, ...

微信小程序与Java后台的通信

一.写在前面 最近接触了小程序的开发,后端选择Java,因为小程序的代码运行在腾讯的服务器上,而我们自己编写的Java代码运行在我们自己部署的服务器上,所以一开始不是很明白小程序如何与后台进行通信的, ...

微信小程序:java后台获取openId

一.功能描述 openId是某个微信账户对应某个小程序或者公众号的唯一标识,但openId必须经过后台解密才能获取(之前实现过前台解密,可是由于微信小程序的种种限制,前台解密无法在小程序发布后使用) ...

微信小程序与java后台交互

java后台使用的ssm框架,小程序连接的本地接口.跟正常的web访问没什么区别,也是后台获取url,返回json数据:只是小程序前台请求的url要带上http://localhost:80801. ...

【原创】微信小程序支付java后台案例(公众号支付同适用)(签名错误问题)

前言 1.微信小程序支付官方接口文档:[点击查看微信开放平台api开发文档]2.遇到的坑:预支付统一下单签名结果返回[签名错误]失败,建议用官方[签名验证工具]检查签名是否存在问题.3.遇到的坑:签名 ...

基于Shiro&comma;JWT实现微信小程序登录完整例子

小程序官方流程图如下,官方地址 : https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html ...

微信小程序登录,获取code,获取openid,获取session&lowbar;key

微信小程序登录 wx.login(Object object) 调用接口获取登录凭证(code).通过凭证进而换取用户登录态信息,包括用户的唯一标识(openid)及本次登录的会话密钥(session ...

Taro -- 微信小程序登录

Taro微信小程序登录 1.调用Taro.login()获取登录凭证code: 2.调用Taro.request()将code传到服务器: 3.服务器端调用微信登录校验接口(appid+appsecr ...

微信小程序需要https后台的创业机会思考

最近比较关注微信小程序,而且微信小程序的后台必须强制要求https, https相对http成本要高很多了. 这里我感觉有2个商机 (1)提供https 中转服务器 ,按流量来收费 (2) 微信小程序 ...

随机推荐

Write Your software base on plugin&lpar;C&sol;C&plus;&plus; ABI&rpar;

一个软件,如果把所有的功能写进C++源码,维护,扩展,编译都特别麻烦. 共享库后缀名.Linux -> .so  Windows -> .dll 关于动态符号显示问题,具体可以看系统的AP ...

理解CSS盒子模型

概述 网页设计中常听的属性名:内容(content).填充(padding).边框(border).边界(margin),CSS盒子模型都具备这些属性,也主要是这些属性. 这些属性我们可以把它转移到我 ...

iphone6闪存检测

iPhone6自从发布以后一直又不少的诟病和非议,比如一机难求,容易掰弯,程序崩溃等, 甚至传出了苹果将要召回这些问题设备,最近有人终于查出了iPhone6安装大量程序后崩溃的原因,原因就是大容量的i ...

wxPython制作跑monkey工具&lpar;python3&rpar;

一. wxPython制作跑monkey工具python文件源代码内容Run Monkey.py如下: #!/usr/bin/env python import wx import os import ...

EtherCAT主站对PHY有要求?

/********************************************************************** * EtherCAT主站对PHY有要求? * 说明: * ...

Java IO(三)——字节流

一.流类 Java的流式输入/输出是建立在四个抽象类的基础上的:InputStream.OutputStream.Reader.Writer.它们用来创建具体的流式子类.尽管程序通过具体子类执行输入/ ...

Codeforces 1154E Two Teams

题目链接:http://codeforces.com/problemset/problem/1154/E 题目大意: 有n个队员,编号1~n,每个人的能力各自对应1~n中的一个数,每个人的能力都不相同 ...

ARM Cortex-A9 &lpar;tiny 4412&rpar;

要求 移植linux增加系统调用并烧写至开发板 详细步骤 一.搭建linux编译环境 1.GCC 编译器的安装: tar xzvf arm-linux-gcc-4.5.1-v6-vfp-2012030 ...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值