微信点餐之后台扫码登录(11)
微信开放平台:https://open.weixin.qq.com/ 扫描登录不支持个人账号
最终的微信登录事件的地址为:
http://applenst.natapp4.cc/sell/wechat/qrAuthorize?returnUrl=applenst.natapp4.cc/sell/seller/login
不走登录授权的测试地址为
http://applenst.natapp4.cc/sell/seller/login?openid=123qwe
新增卖家账号
创建javabean
package com.imooc.vo.admin;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* @author: menghaibin
* @create: 2020-02-26 15:24
* @description: 卖家信息
**/
Data
Entity
public class SellerInfo {
@Id
@Column(name = "seller_id")
private String sellerId;
@Column(name = "username")
private String username;
@Column(name = "password")
private String password;
@Column(name = "openid")
private String openid;
}
创建dao层接口
com\imooc\dao\SellerInfoDao.java
package com.imooc.dao;
import com.imooc.vo.admin.SellerInfo;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* Created by Administrator on 2020/2/26.
*/
public interface SellerInfoDao extends JpaRepository<SellerInfo,String>{
public SellerInfo findByOpenid(String openid);
public SellerInfo save(SellerInfo sellerInfo);
}
创建测试类
package com.imooc.dao;
import com.imooc.utils.JsonUtil;
import com.imooc.vo.admin.SellerInfo;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Created by Administrator on 2020/2/26.
*/
RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class SellerInfoDaoTest {
@Autowired
SellerInfoDao sellerInfoDao;
@Test
public void findByOpenid() throws Exception {
SellerInfo sellerInfo = sellerInfoDao.findByOpenid("123qwe");
log.info(JsonUtil.toJson(sellerInfo));
}
@Test
public void save() throws Exception {
SellerInfo sellerInfo = new SellerInfo();
sellerInfo.setSellerId("123456");
sellerInfo.setUsername("admin");
sellerInfo.setPassword("admin");
sellerInfo.setOpenid("123qwe");
sellerInfoDao.save(sellerInfo);
}
}
卖家扫码(获取到openid)
创建service层接口
com\imooc\service\SellerService.java
package com.imooc.service;
import com.imooc.vo.admin.SellerInfo;
public interface SellerService {
/*通过openid查询卖家端信息*/
public SellerInfo findSellerInfoByOpenid(String openid);
}
创建service层接口的实现类
com\imooc\service\imp\SellerServiceImp.java
package com.imooc.service.imp;
import com.imooc.dao.SellerInfoDao;
import com.imooc.service.SellerService;
import com.imooc.vo.admin.SellerInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author: menghaibin
* @create: 2020-02-26 15:39
* @description: 卖家端登录
**/
@Service
public class SellerServiceImp implements SellerService{
@Autowired
private SellerInfoDao sellerInfoDao;
@Override
public SellerInfo findSellerInfoByOpenid(String openid) {
return sellerInfoDao.findByOpenid(openid);
}
}
在application.yml文件中追加appid以及Secret
#开发平台接入的公众号appid
openAppId: wxf1687xxxxxxxxxxb
#开发平台接入的公众号Secret
openAppSecret: b65d46d51xxxxxxxxxxxxxxxx6ff886d71
在WechatAccountConfig.java文件中追加appid以及Secret属性
/*开放平台微信登录appid*/
private String openAppId;
/*开放平台微信登录app Secret*/
private String openAppSecret;
创建微信登录配置类
com\imooc\config\WeChatOpenConfig.java
package com.imooc.config;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* @author: menghaibin
* @create: 2020-02-26 20:14
* @description: 微信登录配置
**/
@Component
public class WeChatOpenConfig {
@Autowired
private WechatAccountConfig accountConfig;
@Bean
public WxMpService wxOpenService(){
WxMpService wxOpenService = new WxMpServiceImpl();
wxOpenService.setWxMpConfigStorage(wxOpenConfigStorage());
return wxOpenService;
}
@Bean
public WxMpConfigStorage wxOpenConfigStorage(){
WxMpInMemoryConfigStorage wxMpInMemoryConfigStorage = new WxMpInMemoryConfigStorage();
wxMpInMemoryConfigStorage.setAppId(accountConfig.getOpenAppId());
wxMpInMemoryConfigStorage.setSecret(accountConfig.getOpenAppSecret());
return wxMpInMemoryConfigStorage;
}
}
controller实现
在WechatController.java类中追加微信登录qrAuthorize和qrUserInfo的实现,获取openid
/*微信登录*/
@GetMapping("qrAuthorize")
public String qrAuthorize(@RequestParam("returnUrl") String returnUrl){
/*url:授权成功后要跳转的路径*/
String url = accountConfig.getDomainUrl() + "/sell/wechat/qrUserInfo";
String redirectUrl = wxMpService.buildQrConnectUrl(url,WxConsts.QRCONNECT_SCOPE_SNSAPI_LOGIN,URLEncoder.encode(returnUrl));
log.info(redirectUrl);
/*进行跳转*/
return "redirect:"+redirectUrl;
}
/*微信登录回调方法*/
@GetMapping("/qrUserInfo")
public String qrUserInfo(@RequestParam("code") String code,
@RequestParam("state") String returnUrl){
/*code:授权成功后会返回一个code
* state:授权成功后的原始参数*/
WxMpOAuth2AccessToken wxMpOAuth2AccessToken = new WxMpOAuth2AccessToken();
try{
wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
}catch (WxErrorException e){
log.error("微信授权失败");
}
/*得到openid*/
String openId = wxMpOAuth2AccessToken.getOpenId();
/*跳转到系统*/
return "redirect:"+ returnUrl + "?openid="+openId;
}
卖家登录(获取到openid之后)
通过微信登录获取到用户的openid,通过查询数据库是否存在,如果不存在 则跳到错误页,如果存在,则设置一个token到redis 同时把token设置到cookie
1:在application.yml中配置reids链接信息
redis:
host: 192.168.1.106
port: 6379
1:创建微信登录controller
com\imooc\controller\SellerUserController.java
package com.imooc.controller;
import com.imooc.config.WechatAccountConfig;
import com.imooc.exception.SellException;
import com.imooc.service.SellerService;
import com.imooc.utils.CookieUtil;
import com.imooc.vo.admin.SellerInfo;
import com.imooc.vo.enums.ResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @author: menghaibin
* @create: 2020-02-26 20:55
* @description: 卖家后台登录 登出
**/
@Controller
@RequestMapping("/seller")
@Slf4j
public class SellerUserController {
@Autowired
private SellerService sellerService;
/*注解:redis 工具类*/
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private WechatAccountConfig accountConfig;
}
2:在刚才创建的SellerUserController中实现login方法
@GetMapping("/login")
public ModelAndView login(@RequestParam("openid") String openid,
HttpServletResponse response,
Map<String,Object> map){
//1:数据库查询openid是否存在
SellerInfo sellerInfo = sellerService.findSellerInfoByOpenid(openid);
if(sellerInfo == null){
throw new SellException(ResultEnum.LOGIN_FAIL);
}
//2:设置token至redis
String token = UUID.randomUUID().toString();
Integer expire = 7200;//过期时间 2小时
/*参数1:key 格式化 以token_开头
* 参数2:value
* 参数3:过期时间
* 参数4:过期时间的单位
* */
//把openid作为value存入reids
redisTemplate.opsForValue().set(String.format("token_%s",token),openid,expire,TimeUnit.SECONDS);
log.info(String.format("token_%s",token));
//3:设置token至cookie 访问成功后 通过检查可以看到前端有了cookie
CookieUtil.setCookie(response,"token",token,expire);
log.info("成功");
//跳转绝对地址
/*思路:
* 先设置token到redis key=(格式化)token_we23332234434 value=opeind_12345664
* 在设置token到cookie 格式为token:we23332234434
* 验证的时候通过拿到cookie的token值we23332234434去redis获取key([格式化]token_we23332234434)的值opeind_12345664存在不存在
* */
return new ModelAndView("redirect:"+accountConfig.getDomainUrl()+"/sell/seller/order/list");
}
卖家注销
在SellerUserController中实现logout方法
@GetMapping("/logout")
public ModelAndView logout(HttpServletRequest request,HttpServletResponse response,
Map<String,Object> map){
//从cookie查询
Cookie cookie = CookieUtil.getCookie(request,"token");
if(cookie != null){
//清除reids
redisTemplate.opsForValue().getOperations().delete(String.format("token_%s",cookie.getValue()));
//清除cookie
CookieUtil.setCookie(response,"token",null,0);
}
map.put("msg",ResultEnum.LOGOUT_SUCCESS.getMsg());
/*按实际业务跳转*/
map.put("url","/sell/seller/order/list");
return new ModelAndView("common/success",map);
}
请求拦截验证
面向切面技术:取到cookie 在根据cookie得到reids的value 如果存在则不拦截
1:定义一个exception类
com\imooc\exception\SellerAuthorizeException.java
package com.imooc.exception;
/**
* @author: menghaibin
* @create: 2020-02-27 11:34
* @description: 登录校验的异常类 使用者为SellerAuthorizeAspect
**/
public class SellerAuthorizeException extends RuntimeException{
}
2:定义一个切面
com\imooc\aspect\SellerAuthorizeAspect.java
package com.imooc.aspect;
import com.imooc.exception.SellerAuthorizeException;
import com.imooc.utils.CookieUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
/**
* @author: menghaibin
* @create: 2020-02-27 11:21
* @description: 切面 用于token验证
**/
@Component
@Aspect
@Slf4j
public class SellerAuthorizeAspect {
@Autowired
private StringRedisTemplate redisTemplate;
/*定义切入点
* 拦截contorller下的seller开头的类的所有方法 但排除SellerUserController
* */
@Pointcut("execution(public * com.imooc.controller.Seller*.*(..))" +
"&& !execution(public * com.imooc.controller.SellerUserController.*(..))")
public void verify(){};
@Before("verify()")
public void doVerify(){
/*获取HttpServletRequest*/
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
/*查询cookie*/
Cookie cookie = CookieUtil.getCookie(request,"token");
if(cookie == null){
log.warn("[登录校验] cookie中查询不到token");
throw new SellerAuthorizeException();
}
/*查询redis*/
String redisValue = redisTemplate.opsForValue().get(String.format("token_%s",cookie.getValue()));
log.info(redisValue);
if(StringUtils.isEmpty(redisValue)){
log.warn("[登录校验] redis中查询不到token");
throw new SellerAuthorizeException();
}
}
}
3:定义一个捕获异常的handler
com\imooc\handler\SellerExceptionHandler.java
package com.imooc.handler;
import com.imooc.config.WechatAccountConfig;
import com.imooc.exception.SellException;
import com.imooc.exception.SellerAuthorizeException;
import com.imooc.vo.front.ResultVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
/**
* @author: menghaibin
* @create: 2020-02-27 11:51
* @description: 登录校验异常捕获
**/
@ControllerAdvice
public class SellerExceptionHandler {
@Autowired
private WechatAccountConfig accountConfig;
//拦截登录异常
@ExceptionHandler(value = SellerAuthorizeException.class)
public ModelAndView handlerAuthorizeException(){
/*跳转到微信登录地址 http://applenst.natapp4.cc/sell/wechat/qrAuthorize?returnUrl=applenst.natapp4.cc/sell/seller/login*/
return new ModelAndView("redirect:"
.concat(accountConfig.getDomainUrl())
.concat("/sell/wechat/qrAuthorize")
.concat("?returnUrl=")
.concat(accountConfig.getDomainUrl())
.concat("/sell/seller/login"));
}
/*拦截常规异常*/
@ExceptionHandler(value = SellException.class)
@ResponseBody
public ResultVo handlerSellerException(SellException e){
ResultVo resultVo = new ResultVo();
resultVo.setCode(e.getCode());
resultVo.setMsg(e.getMessage());
return resultVo;
}
}