添加微信公众号认证扩展
This commit is contained in:
parent
8718506433
commit
60053c83ef
|
@ -2,6 +2,7 @@ dependencies {
|
|||
|
||||
compile project(":diboot-core")
|
||||
compile project(":diboot-shiro")
|
||||
compile project(":diboot-shiro-wx-mp")
|
||||
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
|
@ -1,11 +1,18 @@
|
|||
package com.diboot.example.controller;
|
||||
|
||||
import com.diboot.core.config.BaseConfig;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.core.vo.JsonResult;
|
||||
import com.diboot.core.vo.Status;
|
||||
import com.diboot.shiro.jwt.BaseJwtAuthenticationToken;
|
||||
import com.diboot.shiro.config.AuthType;
|
||||
import com.diboot.shiro.entity.SysUser;
|
||||
import com.diboot.shiro.service.AuthWayService;
|
||||
import com.diboot.shiro.util.JwtHelper;
|
||||
import com.diboot.shiro.wx.mp.service.WxMpServiceExt;
|
||||
import me.chanjar.weixin.common.api.WxConsts;
|
||||
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
|
||||
import me.chanjar.weixin.mp.bean.result.WxMpUser;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -23,9 +30,14 @@ public class AuthTokenController {
|
|||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthTokenController.class);
|
||||
|
||||
private static final String STATE = BaseConfig.getProperty("wechat.state");
|
||||
|
||||
@Autowired
|
||||
private Map<String, AuthWayService> authWayServiceMap;
|
||||
|
||||
@Autowired
|
||||
private WxMpServiceExt wxMpService;
|
||||
|
||||
/***
|
||||
* 用户名密码登录接口
|
||||
* @param sysUser
|
||||
|
@ -57,6 +69,95 @@ public class AuthTokenController {
|
|||
return new JsonResult(Status.FAIL_INVALID_TOKEN, errorMsg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建OAuth认证链接(为了获取授权所需code)
|
||||
* @param request
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@GetMapping("/buildOAuthUrl")
|
||||
public JsonResult buildOAuthUrl4mp(HttpServletRequest request) throws Exception{
|
||||
String url = request.getParameter("url");
|
||||
if (V.isEmpty(url)){
|
||||
return new JsonResult(Status.FAIL_OPERATION, new String[]{"url为空,获取OAuth链接失败"});
|
||||
}
|
||||
|
||||
String oauthUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAuth2Scope.SNSAPI_USERINFO, STATE);
|
||||
return new JsonResult(Status.OK, oauthUrl, new String[]{"获取OAuth链接成功"});
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信公众号的回调授权登录认证
|
||||
* @param request
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@PostMapping("/apply")
|
||||
public JsonResult applyTokenByOAuth2mp(HttpServletRequest request) throws Exception{
|
||||
String code = request.getParameter("code");
|
||||
String state = request.getParameter("state");
|
||||
String openid = "";
|
||||
if (JwtHelper.isRequestTokenEffective(request)){
|
||||
String account = JwtHelper.getAccountFromToken(JwtHelper.getRequestToken(request));
|
||||
if (account == null){
|
||||
// 如果有code并且token已过期,则使用code获取openid
|
||||
if (V.isEmpty(code)){
|
||||
return new JsonResult(Status.FAIL_INVALID_TOKEN, new String[]{"token已过期"});
|
||||
}
|
||||
} else {
|
||||
openid = account;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果openid没有通过token获取到,则通过code获取
|
||||
if (V.isEmpty(openid)){
|
||||
// 校验STATE
|
||||
if (V.notEmpty(STATE) && !STATE.equals(state)){
|
||||
return new JsonResult(Status.FAIL_OPERATION, new String[]{"非法来源"});
|
||||
}
|
||||
// 获取wxMpService
|
||||
WxMpOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
|
||||
if (!wxMpService.oauth2validateAccessToken(wxMpOAuth2AccessToken)){
|
||||
wxMpOAuth2AccessToken = wxMpService.oauth2refreshAccessToken(wxMpOAuth2AccessToken.getRefreshToken());
|
||||
}
|
||||
WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null);
|
||||
|
||||
openid = wxMpUser.getOpenId();
|
||||
}
|
||||
|
||||
// 如果没有获取到wechat,则返回提示信息
|
||||
if (V.isEmpty(openid)){
|
||||
return new JsonResult(Status.FAIL_INVALID_TOKEN, new String[]{"获取信息失败"});
|
||||
}
|
||||
|
||||
// 设置token
|
||||
BaseJwtAuthenticationToken authToken = new BaseJwtAuthenticationToken(authWayServiceMap, openid, AuthType.WX_MP);
|
||||
// 获取当前的Subject
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
String token = null;
|
||||
String errorMsg = null;
|
||||
|
||||
try {
|
||||
subject.login(authToken);
|
||||
//验证是否登录成功
|
||||
if(subject.isAuthenticated()){
|
||||
token = (String)authToken.getCredentials();
|
||||
logger.debug("openid[" + openid + "]申请token成功!authtoken="+token);
|
||||
}
|
||||
}
|
||||
catch(Exception e){
|
||||
logger.error("登录失败", e);
|
||||
}
|
||||
|
||||
if (V.isEmpty(token)){
|
||||
String msg = V.notEmpty(errorMsg) ? errorMsg : "申请token失败";
|
||||
return new JsonResult(Status.FAIL_INVALID_TOKEN, new String[]{msg});
|
||||
}
|
||||
|
||||
return new JsonResult(Status.OK, token, new String[]{"申请token成功"});
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public JsonResult logout(HttpServletRequest request, HttpServletResponse response) throws Exception{
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
@ -66,4 +167,5 @@ public class AuthTokenController {
|
|||
|
||||
return new JsonResult(Status.OK, new String[]{"退出登录成功"});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group 'com.diboot'
|
||||
version '2.0-alpha'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
compile project(":diboot-core")
|
||||
compile project(":diboot-shiro")
|
||||
|
||||
// 微信开发组件
|
||||
compile("com.github.binarywang:weixin-java-mp:3.2.0")
|
||||
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.diboot.shiro.wx.mp.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/***
|
||||
* @author Wangyl
|
||||
* @version v2.0
|
||||
* @date 2019/6/10
|
||||
*/
|
||||
@Data
|
||||
public class WxMpMember extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = -106928701430810778L;
|
||||
|
||||
@TableField
|
||||
private String openid;
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.diboot.shiro.wx.mp.mapper;
|
||||
|
||||
import com.diboot.core.mapper.BaseCrudMapper;
|
||||
import com.diboot.shiro.wx.mp.entity.WxMpMember;
|
||||
|
||||
/***
|
||||
* @author Wangyl
|
||||
* @version v2.0
|
||||
* @date 2019/6/10
|
||||
*/
|
||||
public interface WxMpMemberMapper extends BaseCrudMapper<WxMpMember> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "./mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.diboot.shiro.wx.mp.mapper.WxMpMemberMapper">
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,13 @@
|
|||
package com.diboot.shiro.wx.mp.service;
|
||||
|
||||
import com.diboot.core.service.BaseService;
|
||||
import com.diboot.shiro.wx.mp.entity.WxMpMember;
|
||||
|
||||
/***
|
||||
* @author Wangyl
|
||||
* @version v2.0
|
||||
* @date 2019/6/10
|
||||
*/
|
||||
public interface WxMpMemberService extends BaseService<WxMpMember> {
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.diboot.shiro.wx.mp.service;
|
||||
|
||||
import com.diboot.core.config.BaseConfig;
|
||||
import com.diboot.core.util.V;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.session.WxSessionManager;
|
||||
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
|
||||
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
|
||||
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
|
||||
import me.chanjar.weixin.mp.api.WxMpService;
|
||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class WxMpServiceExt extends WxMpServiceImpl {
|
||||
private static final Logger logger = LoggerFactory.getLogger(WxMpServiceExt.class);
|
||||
|
||||
private WxMpMessageRouter wxMpMessageRouter;
|
||||
|
||||
private static final String appId = BaseConfig.getProperty("wechat.appId");
|
||||
private static final String secret = BaseConfig.getProperty("wechat.secret");
|
||||
private static final String token = BaseConfig.getProperty("wechat.token");
|
||||
private static final String aesKey = BaseConfig.getProperty("wechat.aesKey");
|
||||
|
||||
@PostConstruct
|
||||
public void init(){
|
||||
WxMpInMemoryConfigStorage inMemoryConfigStorage = new WxMpInMemoryConfigStorage();
|
||||
|
||||
if (V.isEmpty(appId) || V.isEmpty(secret)){
|
||||
logger.warn("服务号相关的appid或secret为空,请检查application.properties配置文件");
|
||||
}
|
||||
|
||||
inMemoryConfigStorage.setAppId(appId);
|
||||
inMemoryConfigStorage.setSecret(secret);
|
||||
inMemoryConfigStorage.setToken(token);
|
||||
inMemoryConfigStorage.setAesKey(aesKey);
|
||||
|
||||
setWxMpConfigStorage(inMemoryConfigStorage);
|
||||
|
||||
wxMpMessageRouter = new WxMpMessageRouter(this);
|
||||
wxMpMessageRouter.rule().handler(new WxMpMessageHandler() {
|
||||
@Override
|
||||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException {
|
||||
logger.info("\n接收到 {} 公众号请求消息,内容:{}", wxMpService.getWxMpConfigStorage().getAppId(), wxMpXmlMessage);
|
||||
return null;
|
||||
}
|
||||
}).next();
|
||||
}
|
||||
|
||||
public WxMpMessageRouter getWxMpMessageRouter(){
|
||||
return wxMpMessageRouter;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package com.diboot.shiro.wx.mp.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.shiro.config.AuthType;
|
||||
import com.diboot.shiro.jwt.BaseJwtAuthenticationToken;
|
||||
import com.diboot.shiro.service.AuthWayService;
|
||||
import com.diboot.shiro.wx.mp.entity.WxMpMember;
|
||||
import com.diboot.shiro.wx.mp.service.WxMpMemberService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/***
|
||||
* 微信公众号认证实现
|
||||
* @author Wangyl
|
||||
* @version v2.0
|
||||
* @date 2019/6/10
|
||||
*/
|
||||
@Service
|
||||
public class WxMpAuthWayServiceImpl implements AuthWayService {
|
||||
|
||||
@Autowired
|
||||
private WxMpMemberService wxMpMemberService;
|
||||
|
||||
private AuthType authType = AuthType.WX_MP;
|
||||
|
||||
private BaseJwtAuthenticationToken token;
|
||||
|
||||
@Override
|
||||
public AuthType authType() {
|
||||
return authType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initByToken(BaseJwtAuthenticationToken token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseEntity getUser() {
|
||||
QueryWrapper<WxMpMember> query = new QueryWrapper();
|
||||
query.lambda()
|
||||
.eq(WxMpMember::getOpenid, token.getAccount());
|
||||
|
||||
List<WxMpMember> wxMpMemberList = wxMpMemberService.getEntityList(query);
|
||||
if (V.isEmpty(wxMpMemberList)){
|
||||
return null;
|
||||
}
|
||||
return wxMpMemberList.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requirePassword() {
|
||||
return authType.isRequirePassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPasswordMatch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPreliminaryVerified() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpiresInMinutes() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.diboot.shiro.wx.mp.service.impl;
|
||||
|
||||
import com.diboot.core.service.impl.BaseServiceImpl;
|
||||
import com.diboot.shiro.wx.mp.entity.WxMpMember;
|
||||
import com.diboot.shiro.wx.mp.mapper.WxMpMemberMapper;
|
||||
import com.diboot.shiro.wx.mp.service.WxMpMemberService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/***
|
||||
* @author Wangyl
|
||||
* @version v2.0
|
||||
* @date 2019/6/10
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class WxMpMemberServiceImpl extends BaseServiceImpl<WxMpMemberMapper, WxMpMember> implements WxMpMemberService {
|
||||
|
||||
}
|
|
@ -53,16 +53,7 @@ public class ShiroConfig {
|
|||
//用户访问未对其授权的资源时的错误提示页面
|
||||
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
|
||||
|
||||
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
|
||||
|
||||
filterChainDefinitionMap.put("/", "anon");
|
||||
filterChainDefinitionMap.put("/static/**", "anon");
|
||||
filterChainDefinitionMap.put("/auth/login", "anon");
|
||||
filterChainDefinitionMap.put("/error", "anon");
|
||||
filterChainDefinitionMap.put("/auth/logout", "logout");
|
||||
filterChainDefinitionMap.put("/**", "jwt");
|
||||
|
||||
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
|
||||
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinition());
|
||||
|
||||
// 设置过滤器
|
||||
Map<String, Filter> filters = new LinkedHashMap<>();
|
||||
|
@ -73,6 +64,22 @@ public class ShiroConfig {
|
|||
return shiroFilterFactoryBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Map filterChainDefinition(){
|
||||
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
|
||||
|
||||
filterChainDefinitionMap.put("/", "anon");
|
||||
filterChainDefinitionMap.put("/static/**", "anon");
|
||||
filterChainDefinitionMap.put("/auth/login", "anon");
|
||||
filterChainDefinitionMap.put("/auth/buildOAuthUrl", "anon");
|
||||
filterChainDefinitionMap.put("/auth/apply", "anon");
|
||||
filterChainDefinitionMap.put("/error", "anon");
|
||||
filterChainDefinitionMap.put("/auth/logout", "logout");
|
||||
filterChainDefinitionMap.put("/**", "jwt");
|
||||
|
||||
return filterChainDefinitionMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shiro生命周期处理器
|
||||
*/
|
||||
|
|
|
@ -11,5 +11,6 @@ rootProject.name = 'diboot-v2'
|
|||
include 'diboot-core'
|
||||
include 'diboot-example'
|
||||
include 'diboot-shiro'
|
||||
include 'diboot-shiro-wx-mp'
|
||||
include 'diboot-docs'
|
||||
|
||||
|
|
Loading…
Reference in New Issue