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
```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
```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
}
```
#### 相关实体类对象
```java
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
```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
```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接口类
```java
@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 ResponseEntitycreateAuthenticationToken(@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);
}
}
```
#### 注册核心流程
```java
@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() 获取用户的基本信息,例如:昵称、头像等,上传本地服务器保存用户基本信息。
```javascript
// 登录
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);
}
}
})
```
#### 效果展示
- 刷新微信小程序缓存,编译使发送请求
![](/contentImages/image/20180328/Z2904SBtpht1pmr9dYe.png)
- 发送登录请求,完成后获取到 access_token
![](/contentImages/image/20180328/h4OWmn4NKKb1g8ZdBKM.png)
- 发送获取用户信息请求
![](/contentImages/image/20180328/vcUk7dU9u1nyxqdOwbt.png)
- 小程序请求本地服务器登录接口
![](/contentImages/image/20180328/ZJ9jigS0YnoyvRjOwCi.png)
- 本地服务器请求微信服务器登录接口
![](/contentImages/image/20180328/XeweBaYrSQ5jGO1TeKE.png)
- 小程序请求本地服务器更新用户信息接口
![](/contentImages/image/20180328/SH6gJiYX6AmCOXHlwy7.png)
- redis保存会话信息
![](/contentImages/image/20180328/kQ98j6DbDMMZMhgdFrC.png)
- mysql数据库存储用户信息
![](/contentImages/image/20180328/UwHA69wKUF2FmoJOXsS.png)