SpringBoot 中的微信登录、已有微信账号绑定用户名密码本地账号、加密解密字符串、分页、Echarts 图表、主从数据库读写分离拦截器、全局异常处理器
2021/4/13 19:25:11
本文主要是介绍SpringBoot 中的微信登录、已有微信账号绑定用户名密码本地账号、加密解密字符串、分页、Echarts 图表、主从数据库读写分离拦截器、全局异常处理器,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1、微信登录
application.properties 配置
#微信相关 wechat.appid=wxd7f6c5b8899fba83 wechat.prefix=https://open.weixin.qq.com/connect/oauth2/authorize?appid=${wechat.appid}&redirect_uri= wechat.middle=&response_type=code&scope=snsapi_userinfo&state= wechat.suffix=#wechat_redirect wechat.auth.url=http://o2o.yitiaojieinfo.com/o2o/shopadmin/addshopauthmap wechat.login.url=http://o2o.yitiaojieinfo.com/o2o/wechatlogin/logincheck wechat.exchange.url=http://o2o.yitiaojieinfo.com/o2o/shopadmin/exchangeaward wechat.productmap.url=http://o2o.yitiaojieinfo.com/o2o/shopadmin/adduserproductmap
WechatAuth.java
package com.imooc.o2o.entity; import java.util.Date; /** * 微信登录实体类 */ public class WechatAuth { // 主键ID private Long wechatAuthId; // 微信获取用户信息的凭证,对于某个公众号具有唯一性 private String openId; // 创建时间 private Date createTime; // 用户信息 private PersonInfo personInfo; public Long getWechatAuthId() { return wechatAuthId; } public void setWechatAuthId(Long wechatAuthId) { this.wechatAuthId = wechatAuthId; } public String getOpenId() { return openId; } public void setOpenId(String openId) { this.openId = openId; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public PersonInfo getPersonInfo() { return personInfo; } public void setPersonInfo(PersonInfo personInfo) { this.personInfo = personInfo; } }
WechatUser.java
package com.imooc.o2o.dto; import java.io.Serializable; import com.fasterxml.jackson.annotation.JsonProperty; /** * 微信用户实体类 */ public class WechatUser implements Serializable { private static final long serialVersionUID = -4684067645282292327L; // openId,标识该公众号下面的该用户的唯一Id @JsonProperty("openid") private String openId; // 用户昵称 @JsonProperty("nickname") private String nickName; // 性别 @JsonProperty("sex") private int sex; // 省份 @JsonProperty("province") private String province; // 城市 @JsonProperty("city") private String city; // 区 @JsonProperty("country") private String country; // 头像图片地址 @JsonProperty("headimgurl") private String headimgurl; // 语言 @JsonProperty("language") private String language; // 用户权限,这里没什么作用 @JsonProperty("privilege") private String[] privilege; public String getOpenId() { return openId; } public void setOpenId(String openId) { this.openId = openId; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getHeadimgurl() { return headimgurl; } public void setHeadimgurl(String headimgurl) { this.headimgurl = headimgurl; } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } public String[] getPrivilege() { return privilege; } public void setPrivilege(String[] privilege) { this.privilege = privilege; } @Override public String toString() { return "openId:" + this.getOpenId() + ",nikename:" + this.getNickName(); } }
WechatInfo.java
package com.imooc.o2o.dto; /** * 用来接收平台二维码的信息 */ public class WechatInfo { private Long customerId; private Long productId; private Long userAwardId; private Long createTime; private Long shopId; public Long getCustomerId() { return customerId; } public void setCustomerId(Long customerId) { this.customerId = customerId; } public Long getProductId() { return productId; } public void setProductId(Long productId) { this.productId = productId; } public Long getUserAwardId() { return userAwardId; } public void setUserAwardId(Long userAwardId) { this.userAwardId = userAwardId; } public Long getShopId() { return shopId; } public void setShopId(Long shopId) { this.shopId = shopId; } public Long getCreateTime() { return createTime; } public void setCreateTime(Long createTime) { this.createTime = createTime; } }
UserAccessToken.java
package com.imooc.o2o.dto; import com.fasterxml.jackson.annotation.JsonProperty; /** * 用户授权token */ public class UserAccessToken { // 获取到的凭证 @JsonProperty("access_token") private String accessToken; // 凭证有效时间,单位:秒 @JsonProperty("expires_in") private String expiresIn; // 表示更新令牌,用来获取下一次的访问令牌,这里没太大用处 @JsonProperty("refresh_token") private String refreshToken; // 该用户在此公众号下的身份标识,对于此微信号具有唯一性 @JsonProperty("openid") private String openId; // 表示权限范围,这里可省略 @JsonProperty("scope") private String scope; public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken; } public String getExpiresIn() { return expiresIn; } public void setExpiresIn(String expiresIn) { this.expiresIn = expiresIn; } public String getRefreshToken() { return refreshToken; } public void setRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } public String getOpenId() { return openId; } public void setOpenId(String openId) { this.openId = openId; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } @Override public String toString() { return "accessToken:" + this.getAccessToken() + ",openId:" + this.getOpenId(); } }
WechatAuthStateEnum.java
package com.imooc.o2o.enums; public enum WechatAuthStateEnum { LOGINFAIL(-1, "openId输入有误"), SUCCESS(0, "操作成功"), NULL_AUTH_INFO(-1006, "注册信息为空"); private int state; private String stateInfo; private WechatAuthStateEnum(int state, String stateInfo) { this.state = state; this.stateInfo = stateInfo; } public int getState() { return state; } public String getStateInfo() { return stateInfo; } public static WechatAuthStateEnum stateOf(int index) { for (WechatAuthStateEnum state : values()) { if (state.getState() == index) { return state; } } return null; } }
WechatAuthExecution.java
package com.imooc.o2o.dto; import java.util.List; import com.imooc.o2o.entity.WechatAuth; import com.imooc.o2o.enums.WechatAuthStateEnum; public class WechatAuthExecution { // 结果状态 private int state; // 状态标识 private String stateInfo; private int count; private WechatAuth wechatAuth; private List<WechatAuth> wechatAuthList; public WechatAuthExecution() { } // 失败的构造器 public WechatAuthExecution(WechatAuthStateEnum stateEnum) { this.state = stateEnum.getState(); this.stateInfo = stateEnum.getStateInfo(); } // 成功的构造器 public WechatAuthExecution(WechatAuthStateEnum stateEnum, WechatAuth wechatAuth) { this.state = stateEnum.getState(); this.stateInfo = stateEnum.getStateInfo(); this.wechatAuth = wechatAuth; } // 成功的构造器 public WechatAuthExecution(WechatAuthStateEnum stateEnum, List<WechatAuth> wechatAuthList) { this.state = stateEnum.getState(); this.stateInfo = stateEnum.getStateInfo(); this.wechatAuthList = wechatAuthList; } public int getState() { return state; } public void setState(int state) { this.state = state; } public String getStateInfo() { return stateInfo; } public void setStateInfo(String stateInfo) { this.stateInfo = stateInfo; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public WechatAuth getWechatAuth() { return wechatAuth; } public void setWechatAuth(WechatAuth wechatAuth) { this.wechatAuth = wechatAuth; } public List<WechatAuth> getWechatAuthList() { return wechatAuthList; } public void setWechatAuthList(List<WechatAuth> wechatAuthList) { this.wechatAuthList = wechatAuthList; } }
WechatAuthOperationException.java
package com.imooc.o2o.exceptions; public class WechatAuthOperationException extends RuntimeException { private static final long serialVersionUID = -4290016045533442745L; public WechatAuthOperationException(String msg) { super(msg); } }
WechatAuthDao.java
package com.imooc.o2o.dao; import com.imooc.o2o.entity.WechatAuth; public interface WechatAuthDao { /** * 通过openId查询对应本平台的微信帐号 * * @param openId * @return */ WechatAuth queryWechatInfoByOpenId(String openId); /** * 添加对应本平台的微信帐号 * * @param wechatAuth * @return */ int insertWechatAuth(WechatAuth wechatAuth); }
WechatAuthDao.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.imooc.o2o.dao.WechatAuthDao"> <resultMap id="wechatAuthResultMap" type="com.imooc.o2o.entity.WechatAuth"> <id column="wechat_auth_id" property="wechatAuthId" /> <result column="open_id" property="openId" /> <result column="create_time" property="createTime" /> <association property="personInfo" column="user_id" javaType="com.imooc.o2o.entity.PersonInfo"> <id column="user_id" property="userId" /> <result column="name" property="name" /> <result column="gender" property="gender" /> <result column="email" property="email" /> <result column="profile_img" property="profileImg" /> <result column="user_type" property="userType" /> <result column="create_time" property="createTime" /> <result column="last_edit_time" property="lastEditTime" /> <result column="enable_status" property="enableStatus" /> </association> </resultMap> <select id="queryWechatInfoByOpenId" resultMap="wechatAuthResultMap" parameterType="String"> SELECT w.wechat_auth_id, w.open_id, w.create_time, p.user_id, p.name, p.gender, p.email, p.profile_img, p.user_type, p.create_time, p.last_edit_time, p.enable_status FROM tb_wechat_auth w LEFT JOIN tb_person_info p ON w.user_id = p.user_id WHERE w.open_id = #{openId} </select> <insert id="insertWechatAuth" parameterType="com.imooc.o2o.entity.WechatAuth" useGeneratedKeys="true" keyProperty="wechatAuthId" keyColumn="wechat_auth_id"> INSERT INTO tb_wechat_auth(user_id,open_id,create_time) VALUES (#{personInfo.userId},#{openId},#{createTime}) </insert> </mapper>
证书信任管理器(用于https请求)MyX509TrustManager.java
package com.imooc.o2o.util.wechat; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; /** * 证书信任管理器(用于https请求) */ public class MyX509TrustManager implements X509TrustManager { /** * 该方法检查客户端的证书,若不信任该证书则抛出异常。 由于我们不需要对客户端进行认证,因此我们只需要执行默认的信任管理器的这个方法。 * JSSE中,默认的信任管理器类为TrustManager */ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } /** * 该方法检查服务器的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。 * 在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。 */ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } /** * 返回受信任的X509证书数组 */ public X509Certificate[] getAcceptedIssuers() { return null; } }
微信请求校验工具类 SignUtil.java
package com.imooc.o2o.util.wechat; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; /** * 微信请求校验工具类 */ public class SignUtil { // 与接口配置信息中的Token要一致 private static String token = "myo2o"; /** * 验证签名 * * @param signature * @param timestamp * @param nonce * @return */ public static boolean checkSignature(String signature, String timestamp, String nonce) { String[] arr = new String[] { token, timestamp, nonce }; // 将token、timestamp、nonce三个参数进行字典序排序 Arrays.sort(arr); StringBuilder content = new StringBuilder(); for (int i = 0; i < arr.length; i++) { content.append(arr[i]); } MessageDigest md = null; String tmpStr = null; try { md = MessageDigest.getInstance("SHA-1"); // 将三个参数字符串拼接成一个字符串进行sha1加密 byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } content = null; // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信 return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false; } /** * 将字节数组转换为十六进制字符串 * * @param byteArray * @return */ private static String byteToStr(byte[] byteArray) { String strDigest = ""; for (int i = 0; i < byteArray.length; i++) { strDigest += byteToHexStr(byteArray[i]); } return strDigest; } /** * 将字节转换为十六进制字符串 * * @param mByte * @return */ private static String byteToHexStr(byte mByte) { char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char[] tempArr = new char[2]; tempArr[0] = Digit[(mByte >>> 4) & 0X0F]; tempArr[1] = Digit[mByte & 0X0F]; String s = new String(tempArr); return s; } }
微信工具类 WechatUtil.java
package com.imooc.o2o.util.wechat; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ConnectException; import java.net.URL; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.imooc.o2o.dto.UserAccessToken; import com.imooc.o2o.dto.WechatUser; import com.imooc.o2o.entity.PersonInfo; /** * 微信工具类 */ public class WechatUtil { private static Logger log = LoggerFactory.getLogger(WechatUtil.class); /** * 获取UserAccessToken实体类 * * @param code * @return * @throws IOException */ public static UserAccessToken getUserAccessToken(String code) throws IOException { // 测试号信息里的appId String appId = "wxd7f6c5b8899fba83"; log.debug("appId:" + appId); // 测试号信息里的appsecret String appsecret = "665ae80dba31fc91ab6191e7da4d676d"; log.debug("secret:" + appsecret); // 根据传入的code,拼接出访问微信定义好的接口的URL String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appsecret + "&code=" + code + "&grant_type=authorization_code"; // 向相应URL发送请求获取token json字符串 String tokenStr = httpsRequest(url, "GET", null); log.debug("userAccessToken:" + tokenStr); UserAccessToken token = new UserAccessToken(); ObjectMapper objectMapper = new ObjectMapper(); try { // 将json字符串转换成相应对象 token = objectMapper.readValue(tokenStr, UserAccessToken.class); } catch (JsonParseException e) { log.error("获取用户accessToken失败: " + e.getMessage()); e.printStackTrace(); } catch (JsonMappingException e) { log.error("获取用户accessToken失败: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { log.error("获取用户accessToken失败: " + e.getMessage()); e.printStackTrace(); } if (token == null) { log.error("获取用户accessToken失败。"); return null; } return token; } /** * 获取WechatUser实体类 * * @param accessToken * @param openId * @return */ public static WechatUser getUserInfo(String accessToken, String openId) { // 根据传入的accessToken以及openId拼接出访问微信定义的端口并获取用户信息的URL String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN"; // 访问该URL获取用户信息json 字符串 String userStr = httpsRequest(url, "GET", null); log.debug("user info :" + userStr); WechatUser user = new WechatUser(); ObjectMapper objectMapper = new ObjectMapper(); try { // 将json字符串转换成相应对象 user = objectMapper.readValue(userStr, WechatUser.class); } catch (JsonParseException e) { log.error("获取用户信息失败: " + e.getMessage()); e.printStackTrace(); } catch (JsonMappingException e) { log.error("获取用户信息失败: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { log.error("获取用户信息失败: " + e.getMessage()); e.printStackTrace(); } if (user == null) { log.error("获取用户信息失败。"); return null; } return user; } /** * 将WechatUser里的信息转换成PersonInfo的信息并返回PersonInfo实体类 * * @param user * @return */ public static PersonInfo getPersonInfoFromRequest(WechatUser user) { PersonInfo personInfo = new PersonInfo(); personInfo.setName(user.getNickName()); personInfo.setGender(user.getSex() + ""); personInfo.setProfileImg(user.getHeadimgurl()); personInfo.setEnableStatus(1); return personInfo; } /** * 发起https请求并获取结果 * @param requestUrl 请求地址 * @param requestMethod 请求方式(GET、POST) * @param outputStr 提交的数据 * @return json字符串 */ public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) { StringBuffer buffer = new StringBuffer(); try { // 创建SSLContext对象,并使用我们指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 从上述SSLContext对象中得到SSLSocketFactory对象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // 设置请求方式(GET/POST) httpUrlConn.setRequestMethod(requestMethod); if ("GET".equalsIgnoreCase(requestMethod)) httpUrlConn.connect(); // 当有数据需要提交时 if (null != outputStr) { OutputStream outputStream = httpUrlConn.getOutputStream(); // 注意编码格式,防止中文乱码 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 将返回的输入流转换成字符串 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 释放资源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); log.debug("https buffer:" + buffer.toString()); } catch (ConnectException ce) { log.error("Weixin server connection timed out."); } catch (Exception e) { log.error("https request error:{}", e); } return buffer.toString(); } }
WechatAuthService.java
package com.imooc.o2o.service; import com.imooc.o2o.dto.WechatAuthExecution; import com.imooc.o2o.entity.WechatAuth; import com.imooc.o2o.exceptions.WechatAuthOperationException; public interface WechatAuthService { /** * 通过openId查找平台对应的微信帐号 * * @param openId * @return */ WechatAuth getWechatAuthByOpenId(String openId); /** * 注册本平台的微信帐号 * * @param wechatAuth * @param profileImg * @return * @throws RuntimeException */ WechatAuthExecution register(WechatAuth wechatAuth) throws WechatAuthOperationException; }
WechatAuthServiceImpl.java
package com.imooc.o2o.service.impl; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.imooc.o2o.dao.PersonInfoDao; import com.imooc.o2o.dao.WechatAuthDao; import com.imooc.o2o.dto.WechatAuthExecution; import com.imooc.o2o.entity.PersonInfo; import com.imooc.o2o.entity.WechatAuth; import com.imooc.o2o.enums.WechatAuthStateEnum; import com.imooc.o2o.exceptions.WechatAuthOperationException; import com.imooc.o2o.service.WechatAuthService; @Service public class WechatAuthServiceImpl implements WechatAuthService { private static Logger log = LoggerFactory.getLogger(WechatAuthServiceImpl.class); @Autowired private WechatAuthDao wechatAuthDao; @Autowired private PersonInfoDao personInfoDao; @Override public WechatAuth getWechatAuthByOpenId(String openId) { return wechatAuthDao.queryWechatInfoByOpenId(openId); } @Override @Transactional public WechatAuthExecution register(WechatAuth wechatAuth) throws WechatAuthOperationException { //空值判断 if (wechatAuth == null || wechatAuth.getOpenId() == null) { return new WechatAuthExecution(WechatAuthStateEnum.NULL_AUTH_INFO); } try { //设置创建时间 wechatAuth.setCreateTime(new Date()); //如果微信帐号里夹带着用户信息并且用户Id为空,则认为该用户第一次使用平台(且通过微信登录) //则自动创建用户信息 if (wechatAuth.getPersonInfo() != null && wechatAuth.getPersonInfo().getUserId() == null) { try { wechatAuth.getPersonInfo().setCreateTime(new Date()); wechatAuth.getPersonInfo().setEnableStatus(1); PersonInfo personInfo = wechatAuth.getPersonInfo(); int effectedNum = personInfoDao.insertPersonInfo(personInfo); wechatAuth.setPersonInfo(personInfo); if (effectedNum <= 0) { throw new WechatAuthOperationException("添加用户信息失败"); } } catch (Exception e) { log.error("insertPersonInfo error:" + e.toString()); throw new WechatAuthOperationException("insertPersonInfo error: " + e.getMessage()); } } //创建专属于本平台的微信帐号 int effectedNum = wechatAuthDao.insertWechatAuth(wechatAuth); if (effectedNum <= 0) { throw new WechatAuthOperationException("帐号创建失败"); } else { return new WechatAuthExecution(WechatAuthStateEnum.SUCCESS, wechatAuth); } } catch (Exception e) { log.error("insertWechatAuth error:" + e.toString()); throw new WechatAuthOperationException("insertWechatAuth error: " + e.getMessage()); } } }
WechatController.java
package com.imooc.o2o.web.wechat; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.imooc.o2o.util.wechat.SignUtil; @Controller @RequestMapping("wechat") public class WechatController { private static Logger log = LoggerFactory.getLogger(WechatController.class); @RequestMapping(method = { RequestMethod.GET }) public void doGet(HttpServletRequest request, HttpServletResponse response) { log.debug("weixin get..."); // 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 String signature = request.getParameter("signature"); // 时间戳 String timestamp = request.getParameter("timestamp"); // 随机数 String nonce = request.getParameter("nonce"); // 随机字符串 String echostr = request.getParameter("echostr"); // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败 PrintWriter out = null; try { out = response.getWriter(); if (SignUtil.checkSignature(signature, timestamp, nonce)) { log.debug("weixin get success...."); out.print(echostr); } } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) out.close(); } } }
WechatLoginController.java
package com.imooc.o2o.web.wechat; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.imooc.o2o.dto.UserAccessToken; import com.imooc.o2o.dto.WechatAuthExecution; import com.imooc.o2o.dto.WechatUser; import com.imooc.o2o.entity.PersonInfo; import com.imooc.o2o.entity.WechatAuth; import com.imooc.o2o.enums.WechatAuthStateEnum; import com.imooc.o2o.service.PersonInfoService; import com.imooc.o2o.service.WechatAuthService; import com.imooc.o2o.util.wechat.WechatUtil; /** * 获取关注公众号之后的微信用户信息的接口,如果在微信浏览器里访问 * https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxd7f6c5b8899fba83&redirect_uri=http://o2o.yitiaojieinfo.com/o2o/wechatlogin/logincheck&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect * 则这里将会获取到code,之后再可以通过code获取到access_token 进而获取到用户信息 * * @author xiangze * */ @Controller @RequestMapping("wechatlogin") public class WechatLoginController { private static Logger log = LoggerFactory.getLogger(WechatLoginController.class); private static final String FRONTEND = "1"; private static final String SHOPEND = "2"; @Autowired private PersonInfoService personInfoService; @Autowired private WechatAuthService wechatAuthService; @RequestMapping(value = "/logincheck", method = { RequestMethod.GET }) public String doGet(HttpServletRequest request, HttpServletResponse response) { log.debug("weixin login get..."); // 获取微信公众号传输过来的code,通过code可获取access_token,进而获取用户信息 String code = request.getParameter("code"); // 这个state可以用来传我们自定义的信息,方便程序调用,这里也可以不用 String roleType = request.getParameter("state"); log.debug("weixin login code:" + code); WechatUser user = null; String openId = null; WechatAuth auth = null; if (null != code) { UserAccessToken token; try { // 通过code获取access_token token = WechatUtil.getUserAccessToken(code); log.debug("weixin login token:" + token.toString()); // 通过token获取accessToken String accessToken = token.getAccessToken(); // 通过token获取openId openId = token.getOpenId(); // 通过access_token和openId获取用户昵称等信息 user = WechatUtil.getUserInfo(accessToken, openId); log.debug("weixin login user:" + user.toString()); request.getSession().setAttribute("openId", openId); auth = wechatAuthService.getWechatAuthByOpenId(openId); } catch (IOException e) { log.error("error in getUserAccessToken or getUserInfo or findByOpenId: " + e.toString()); e.printStackTrace(); } } // 若微信帐号为空则需要注册微信帐号,同时注册用户信息 if (auth == null) { PersonInfo personInfo = WechatUtil.getPersonInfoFromRequest(user); auth = new WechatAuth(); auth.setOpenId(openId); if (FRONTEND.equals(roleType)) { personInfo.setUserType(1); } else { personInfo.setUserType(2); } auth.setPersonInfo(personInfo); WechatAuthExecution we = wechatAuthService.register(auth); if (we.getState() != WechatAuthStateEnum.SUCCESS.getState()) { return null; } else { personInfo = personInfoService.getPersonInfoById(auth.getPersonInfo().getUserId()); request.getSession().setAttribute("user", personInfo); } } else { request.getSession().setAttribute("user", auth.getPersonInfo()); } // 若用户点击的是前端展示系统按钮则进入前端展示系统 if (FRONTEND.equals(roleType)) { return "frontend/index"; } else { return "shop/shoplist"; } } }
2、已有微信账号绑定用户名密码本地账号
LocalAuth.java
package com.imooc.o2o.entity; import java.util.Date; /** * 本地验证实体类,主要用来做帐号密码的登录 * * @author xiangze * */ public class LocalAuth { // 主键ID private Long localAuthId; // 帐号 private String username; // 密码 private String password; // 创建时间 private Date createTime; // 最近一次的更新时间 private Date lastEditTime; // 个人信息,关系为一一对应 private PersonInfo personInfo; public Long getLocalAuthId() { return localAuthId; } public void setLocalAuthId(Long localAuthId) { this.localAuthId = localAuthId; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Date getLastEditTime() { return lastEditTime; } public void setLastEditTime(Date lastEditTime) { this.lastEditTime = lastEditTime; } public PersonInfo getPersonInfo() { return personInfo; } public void setPersonInfo(PersonInfo personInfo) { this.personInfo = personInfo; } }
LocalAuthDao.java
package com.imooc.o2o.dao; import java.util.Date; import org.apache.ibatis.annotations.Param; import com.imooc.o2o.entity.LocalAuth; public interface LocalAuthDao { /** * 通过帐号和密码查询对应信息,登录用 * * @param username * @param password * @return */ LocalAuth queryLocalByUserNameAndPwd(@Param("username") String username, @Param("password") String password); /** * 通过用户Id查询对应localauth * * @param userId * @return */ LocalAuth queryLocalByUserId(@Param("userId") long userId); /** * 添加平台帐号 * * @param localAuth * @return */ int insertLocalAuth(LocalAuth localAuth); /** * 通过userId,username,password更改密码 * * @param localAuth * @return */ int updateLocalAuth(@Param("userId") Long userId, @Param("username") String username, @Param("password") String password, @Param("newPassword") String newPassword, @Param("lastEditTime") Date lastEditTime); }
LocalAuthDao.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.imooc.o2o.dao.LocalAuthDao"> <resultMap id="localAuthResultMap" type="com.imooc.o2o.entity.LocalAuth"> <id column="local_auth_id" property="localAuthId" /> <result column="username" property="username" /> <result column="password" property="password" /> <result column="create_time" property="createTime" /> <result column="last_edit_time" property="lastEditTime" /> <association property="personInfo" column="user_id" javaType="com.imooc.o2o.entity.PersonInfo"> <id column="user_id" property="userId" /> <result column="name" property="name" /> <result column="gender" property="gender" /> <result column="email" property="email" /> <result column="profile_img" property="profileImg" /> <result column="user_type" property="userType" /> <result column="create_time" property="createTime" /> <result column="last_edit_time" property="lastEditTime" /> <result column="enable_status" property="enableStatus" /> </association> </resultMap> <select id="queryLocalByUserNameAndPwd" resultMap="localAuthResultMap" parameterType="String"> SELECT l.local_auth_id, l.username, l.password, l.create_time, l.last_edit_time, p.user_id, p.name, p.gender, p.email, p.profile_img, p.user_type, p.create_time, p.last_edit_time, p.enable_status FROM tb_local_auth l LEFT JOIN tb_person_info p ON l.user_id = p.user_id WHERE l.username = #{username} AND l.password = #{password} </select> <select id="queryLocalByUserId" resultMap="localAuthResultMap" parameterType="long"> SELECT l.local_auth_id, l.username, l.password, l.create_time, l.last_edit_time, p.user_id, p.name, p.gender, p.email, p.profile_img, p.user_type, p.create_time, p.last_edit_time, p.enable_status FROM tb_local_auth l LEFT JOIN tb_person_info p ON l.user_id = p.user_id WHERE l.user_id = #{userId} </select> <insert id="insertLocalAuth" parameterType="com.imooc.o2o.entity.LocalAuth" useGeneratedKeys="true" keyProperty="localAuthId" keyColumn="local_auth_id"> INSERT INTO tb_local_auth(username,password,user_id,create_time,last_edit_time) VALUES (#{username},#{password},#{personInfo.userId},#{createTime},#{lastEditTime}) </insert> <update id="updateLocalAuth"> update tb_local_auth <set> <if test="newPassword != null">password=#{newPassword},</if> <if test="lastEditTime != null">last_edit_time=#{lastEditTime}</if> </set> where user_id=#{userId} AND username=#{username} AND password=#{password} </update> </mapper>
LocalAuthStateEnum.java
package com.imooc.o2o.enums; public enum LocalAuthStateEnum { LOGINFAIL(-1, "密码或帐号输入有误"), SUCCESS(0, "操作成功"), NULL_AUTH_INFO(-1006, "注册信息为空"), ONLY_ONE_ACCOUNT(-1007,"最多只能绑定一个本地帐号"); private int state; private String stateInfo; private LocalAuthStateEnum(int state, String stateInfo) { this.state = state; this.stateInfo = stateInfo; } public int getState() { return state; } public String getStateInfo() { return stateInfo; } public static LocalAuthStateEnum stateOf(int index) { for (LocalAuthStateEnum state : values()) { if (state.getState() == index) { return state; } } return null; } }
LocalAuthExecution.java
package com.imooc.o2o.dto; import java.util.List; import com.imooc.o2o.entity.LocalAuth; import com.imooc.o2o.enums.LocalAuthStateEnum; public class LocalAuthExecution { // 结果状态 private int state; // 状态标识 private String stateInfo; private int count; private LocalAuth localAuth; private List<LocalAuth> localAuthList; public LocalAuthExecution() { } // 失败的构造器 public LocalAuthExecution(LocalAuthStateEnum stateEnum) { this.state = stateEnum.getState(); this.stateInfo = stateEnum.getStateInfo(); } // 成功的构造器 public LocalAuthExecution(LocalAuthStateEnum stateEnum, LocalAuth localAuth) { this.state = stateEnum.getState(); this.stateInfo = stateEnum.getStateInfo(); this.localAuth = localAuth; } // 成功的构造器 public LocalAuthExecution(LocalAuthStateEnum stateEnum, List<LocalAuth> localAuthList) { this.state = stateEnum.getState(); this.stateInfo = stateEnum.getStateInfo(); this.localAuthList = localAuthList; } public int getState() { return state; } public void setState(int state) { this.state = state; } public String getStateInfo() { return stateInfo; } public void setStateInfo(String stateInfo) { this.stateInfo = stateInfo; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public LocalAuth getLocalAuth() { return localAuth; } public void setLocalAuth(LocalAuth localAuth) { this.localAuth = localAuth; } public List<LocalAuth> getLocalAuthList() { return localAuthList; } public void setLocalAuthList(List<LocalAuth> localAuthList) { this.localAuthList = localAuthList; } }
LocalAuthOperationException.java
package com.imooc.o2o.exceptions; public class LocalAuthOperationException extends RuntimeException { private static final long serialVersionUID = -8260236137099919700L; public LocalAuthOperationException(String msg) { super(msg); } }
LocalAuthService.java
package com.imooc.o2o.service; import com.imooc.o2o.dto.LocalAuthExecution; import com.imooc.o2o.entity.LocalAuth; import com.imooc.o2o.exceptions.LocalAuthOperationException; public interface LocalAuthService { /** * 通过帐号和密码获取平台帐号信息 * * @param userName * @return */ LocalAuth getLocalAuthByUsernameAndPwd(String userName, String password); /** * 通过userId获取平台帐号信息 * * @param userId * @return */ LocalAuth getLocalAuthByUserId(long userId); /** * 绑定微信,生成平台专属的帐号 * * @param localAuth * @return * @throws RuntimeException */ LocalAuthExecution bindLocalAuth(LocalAuth localAuth) throws LocalAuthOperationException; /** * 修改平台帐号的登录密码 * * @param localAuthId * @param userName * @param password * @param newPassword * @param lastEditTime * @return */ LocalAuthExecution modifyLocalAuth(Long userId, String username, String password, String newPassword) throws LocalAuthOperationException; }
LocalAuthServiceImpl.java
package com.imooc.o2o.service.impl; import java.util.Date; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.imooc.o2o.dao.LocalAuthDao; import com.imooc.o2o.dto.LocalAuthExecution; import com.imooc.o2o.entity.LocalAuth; import com.imooc.o2o.enums.LocalAuthStateEnum; import com.imooc.o2o.exceptions.LocalAuthOperationException; import com.imooc.o2o.service.LocalAuthService; import com.imooc.o2o.util.MD5; @Service public class LocalAuthServiceImpl implements LocalAuthService { @Autowired private LocalAuthDao localAuthDao; @Override public LocalAuth getLocalAuthByUsernameAndPwd(String username, String password) { return localAuthDao.queryLocalByUserNameAndPwd(username, MD5.getMd5(password)); } @Override public LocalAuth getLocalAuthByUserId(long userId) { return localAuthDao.queryLocalByUserId(userId); } @Override @Transactional public LocalAuthExecution bindLocalAuth(LocalAuth localAuth) throws LocalAuthOperationException { // 空值判断,传入的localAuth 帐号密码,用户信息特别是userId不能为空,否则直接返回错误 if (localAuth == null || localAuth.getPassword() == null || localAuth.getUsername() == null || localAuth.getPersonInfo() == null || localAuth.getPersonInfo().getUserId() == null) { return new LocalAuthExecution(LocalAuthStateEnum.NULL_AUTH_INFO); } // 查询此用户是否已绑定过平台帐号 LocalAuth tempAuth = localAuthDao.queryLocalByUserId(localAuth.getPersonInfo().getUserId()); if (tempAuth != null) { // 如果绑定过则直接退出,以保证平台帐号的唯一性 return new LocalAuthExecution(LocalAuthStateEnum.ONLY_ONE_ACCOUNT); } try { // 如果之前没有绑定过平台帐号,则创建一个平台帐号与该用户绑定 localAuth.setCreateTime(new Date()); localAuth.setLastEditTime(new Date()); // 对密码进行MD5加密 localAuth.setPassword(MD5.getMd5(localAuth.getPassword())); int effectedNum = localAuthDao.insertLocalAuth(localAuth); // 判断创建是否成功 if (effectedNum <= 0) { throw new LocalAuthOperationException("帐号绑定失败"); } else { return new LocalAuthExecution(LocalAuthStateEnum.SUCCESS, localAuth); } } catch (Exception e) { throw new LocalAuthOperationException("insertLocalAuth error: " + e.getMessage()); } } @Override @Transactional public LocalAuthExecution modifyLocalAuth(Long userId, String userName, String password, String newPassword) throws LocalAuthOperationException { // 非空判断,判断传入的用户Id,帐号,新旧密码是否为空,新旧密码是否相同,若不满足条件则返回错误信息 if (userId != null && userName != null && password != null && newPassword != null && !password.equals(newPassword)) { try { // 更新密码,并对新密码进行MD5加密 int effectedNum = localAuthDao.updateLocalAuth(userId, userName, MD5.getMd5(password), MD5.getMd5(newPassword), new Date()); // 判断更新是否成功 if (effectedNum <= 0) { throw new LocalAuthOperationException("更新密码失败"); } return new LocalAuthExecution(LocalAuthStateEnum.SUCCESS); } catch (Exception e) { throw new LocalAuthOperationException("更新密码失败:" + e.toString()); } } else { return new LocalAuthExecution(LocalAuthStateEnum.NULL_AUTH_INFO); } } }
LocalController.java
package com.imooc.o2o.web.local; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/local") public class LocalController { /** * 绑定帐号页路由 * * @return */ @RequestMapping(value = "/accountbind", method = RequestMethod.GET) private String accountbind() { return "local/accountbind"; } /** * 修改密码页路由 * * @return */ @RequestMapping(value = "/changepsw", method = RequestMethod.GET) private String changepsw() { return "local/changepsw"; } /** * 登录页路由 * * @return */ @RequestMapping(value = "/login", method = RequestMethod.GET) private String login() { return "local/login"; } }
LocalAuthController.java
package com.imooc.o2o.web.local; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.imooc.o2o.dto.LocalAuthExecution; import com.imooc.o2o.entity.LocalAuth; import com.imooc.o2o.entity.PersonInfo; import com.imooc.o2o.enums.LocalAuthStateEnum; import com.imooc.o2o.exceptions.LocalAuthOperationException; import com.imooc.o2o.service.LocalAuthService; import com.imooc.o2o.util.CodeUtil; import com.imooc.o2o.util.HttpServletRequestUtil; @Controller @RequestMapping(value = "local", method = { RequestMethod.GET, RequestMethod.POST }) public class LocalAuthController { @Autowired private LocalAuthService localAuthService; @RequestMapping(value = "/bindlocalauth", method = RequestMethod.POST) @ResponseBody /** * 将用户信息与平台帐号绑定 * * @param request * @return */ private Map<String, Object> bindLocalAuth(HttpServletRequest request) { Map<String, Object> modelMap = new HashMap<String, Object>(); // 验证码校验 if (!CodeUtil.checkVerifyCode(request)) { modelMap.put("success", false); modelMap.put("errMsg", "输入了错误的验证码"); return modelMap; } // 获取输入的帐号 String userName = HttpServletRequestUtil.getString(request, "userName"); // 获取输入的密码 String password = HttpServletRequestUtil.getString(request, "password"); // 从session中获取当前用户信息(用户一旦通过微信登录之后,便能获取到用户的信息) PersonInfo user = (PersonInfo) request.getSession().getAttribute("user"); // 非空判断,要求帐号密码以及当前的用户session非空 if (userName != null && password != null && user != null && user.getUserId() != null) { // 创建LocalAuth对象并赋值 LocalAuth localAuth = new LocalAuth(); localAuth.setUsername(userName); localAuth.setPassword(password); localAuth.setPersonInfo(user); // 绑定帐号 LocalAuthExecution le = localAuthService.bindLocalAuth(localAuth); if (le.getState() == LocalAuthStateEnum.SUCCESS.getState()) { modelMap.put("success", true); } else { modelMap.put("success", false); modelMap.put("errMsg", le.getStateInfo()); } } else { modelMap.put("success", false); modelMap.put("errMsg", "用户名和密码均不能为空"); } return modelMap; } @RequestMapping(value = "/changelocalpwd", method = RequestMethod.POST) @ResponseBody /** * 修改密码 * * @param request * @return */ private Map<String, Object> changeLocalPwd(HttpServletRequest request) { Map<String, Object> modelMap = new HashMap<String, Object>(); // 验证码校验 if (!CodeUtil.checkVerifyCode(request)) { modelMap.put("success", false); modelMap.put("errMsg", "输入了错误的验证码"); return modelMap; } // 获取帐号 String userName = HttpServletRequestUtil.getString(request, "userName"); // 获取原密码 String password = HttpServletRequestUtil.getString(request, "password"); // 获取新密码 String newPassword = HttpServletRequestUtil.getString(request, "newPassword"); // 从session中获取当前用户信息(用户一旦通过微信登录之后,便能获取到用户的信息) PersonInfo user = (PersonInfo) request.getSession().getAttribute("user"); // 非空判断,要求帐号新旧密码以及当前的用户session非空,且新旧密码不相同 if (userName != null && password != null && newPassword != null && user != null && user.getUserId() != null && !password.equals(newPassword)) { try { // 查看原先帐号,看看与输入的帐号是否一致,不一致则认为是非法操作 LocalAuth localAuth = localAuthService.getLocalAuthByUserId(user.getUserId()); if (localAuth == null || !localAuth.getUsername().equals(userName)) { // 不一致则直接退出 modelMap.put("success", false); modelMap.put("errMsg", "输入的帐号非本次登录的帐号"); return modelMap; } // 修改平台帐号的用户密码 LocalAuthExecution le = localAuthService.modifyLocalAuth(user.getUserId(), userName, password, newPassword); if (le.getState() == LocalAuthStateEnum.SUCCESS.getState()) { modelMap.put("success", true); } else { modelMap.put("success", false); modelMap.put("errMsg", le.getStateInfo()); } } catch (LocalAuthOperationException e) { modelMap.put("success", false); modelMap.put("errMsg", e.toString()); return modelMap; } } else { modelMap.put("success", false); modelMap.put("errMsg", "请输入密码"); } return modelMap; } @RequestMapping(value = "/logincheck", method = RequestMethod.POST) @ResponseBody private Map<String, Object> logincheck(HttpServletRequest request) { Map<String, Object> modelMap = new HashMap<String, Object>(); // 获取是否需要进行验证码校验的标识符 boolean needVerify = HttpServletRequestUtil.getBoolean(request, "needVerify"); if (needVerify && !CodeUtil.checkVerifyCode(request)) { modelMap.put("success", false); modelMap.put("errMsg", "输入了错误的验证码"); return modelMap; } // 获取输入的帐号 String userName = HttpServletRequestUtil.getString(request, "userName"); // 获取输入的密码 String password = HttpServletRequestUtil.getString(request, "password"); // 非空校验 if (userName != null && password != null) { // 传入帐号和密码去获取平台帐号信息 LocalAuth localAuth = localAuthService.getLocalAuthByUsernameAndPwd(userName, password); if (localAuth != null) { // 若能取到帐号信息则登录成功 modelMap.put("success", true); // 同时在session里设置用户信息 request.getSession().setAttribute("user", localAuth.getPersonInfo()); } else { modelMap.put("success", false); modelMap.put("errMsg", "用户名或密码错误"); } } else { modelMap.put("success", false); modelMap.put("errMsg", "用户名和密码均不能为空"); } return modelMap; } @RequestMapping(value = "/logout", method = RequestMethod.POST) @ResponseBody /** * 当用户点击登出按钮的时候注销session * * @param request * @return * @throws IOException */ private Map<String, Object> logout(HttpServletRequest request) { Map<String, Object> modelMap = new HashMap<String, Object>(); // 将用户session置为空 request.getSession().setAttribute("user", null); modelMap.put("success", true); return modelMap; } }
3、加密解密字符串
配置文件中存放数据库连接字符串、用户名密码字符串加解密。
DataSourceConfiguration.java
package com.imooc.o2o.config.dao; import java.beans.PropertyVetoException; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.imooc.o2o.util.DESUtil; import com.mchange.v2.c3p0.ComboPooledDataSource; /** * 配置datasource到ioc容器里面 */ @Configuration // 配置mybatis mapper的扫描路径 @MapperScan("com.imooc.o2o.dao") public class DataSourceConfiguration { @Value("${jdbc.driver}") private String jdbcDriver; @Value("${jdbc.url}") private String jdbcUrl; @Value("${jdbc.username}") private String jdbcUsername; @Value("${jdbc.password}") private String jdbcPassword; /** * 生成与spring-dao.xml对应的bean dataSource * * @return * @throws PropertyVetoException */ @Bean(name = "dataSource") public ComboPooledDataSource createDataSource() throws PropertyVetoException { // 生成datasource实例 ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 跟配置文件一样设置以下信息 // 驱动 dataSource.setDriverClass(jdbcDriver); // 数据库连接URL dataSource.setJdbcUrl(jdbcUrl); // 设置用户名 dataSource.setUser(DESUtil.getDecryptString(jdbcUsername)); // 设置用户密码 dataSource.setPassword(DESUtil.getDecryptString(jdbcPassword)); // 配置c3p0连接池的私有属性 // 连接池最大线程数 dataSource.setMaxPoolSize(30); // 连接池最小线程数 dataSource.setMinPoolSize(10); dataSource.setInitialPoolSize(10); // 关闭连接后不自动commit dataSource.setAutoCommitOnClose(false); // 连接超时时间 dataSource.setCheckoutTimeout(10000); // 连接失败重试次数 dataSource.setAcquireRetryAttempts(2); return dataSource; } }
DESUtil.java
package com.imooc.o2o.util; import java.security.Key; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; /** * DES是一种对称加密算法,所谓对称加密算法即:加密和解密使用相同密钥的算法。 */ public class DESUtil { private static Key key; // 设置密钥key private static String KEY_STR = "myKey"; private static String CHARSETNAME = "UTF-8"; private static String ALGORITHM = "DES"; static { try { // 生成DES算法对象 KeyGenerator generator = KeyGenerator.getInstance(ALGORITHM); // 运用SHA1安全策略 SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); // 设置上密钥种子 secureRandom.setSeed(KEY_STR.getBytes()); // 初始化基于SHA1的算法对象 generator.init(secureRandom); // 生成密钥对象 key = generator.generateKey(); generator = null; } catch (Exception e) { throw new RuntimeException(e); } } /** * 获取加密后的信息 * * @param str * @return */ public static String getEncryptString(String str) { // 基于BASE64编码,接收byte[]并转换成String BASE64Encoder base64encoder = new BASE64Encoder(); try { // 按UTF8编码 byte[] bytes = str.getBytes(CHARSETNAME); // 获取加密对象 Cipher cipher = Cipher.getInstance(ALGORITHM); // 初始化密码信息 cipher.init(Cipher.ENCRYPT_MODE, key); // 加密 byte[] doFinal = cipher.doFinal(bytes); // byte[]to encode好的String并返回 return base64encoder.encode(doFinal); } catch (Exception e) { // TODO: handle exception throw new RuntimeException(e); } } /** * 获取解密之后的信息 * * @param str * @return */ public static String getDecryptString(String str) { // 基于BASE64编码,接收byte[]并转换成String BASE64Decoder base64decoder = new BASE64Decoder(); try { // 将字符串decode成byte[] byte[] bytes = base64decoder.decodeBuffer(str); // 获取解密对象 Cipher cipher = Cipher.getInstance(ALGORITHM); // 初始化解密信息 cipher.init(Cipher.DECRYPT_MODE, key); // 解密 byte[] doFinal = cipher.doFinal(bytes); // 返回解密之后的信息 return new String(doFinal, CHARSETNAME); } catch (Exception e) { // TODO: handle exception throw new RuntimeException(e); } } public static void main(String[] args) { System.out.println(getEncryptString("root")); System.out.println(getEncryptString("Xiangze230!")); } }
EncryptPropertyPlaceholderConfigurer.java
package com.imooc.o2o.util; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer { // 需要加密的字段数组 private String[] encryptPropNames = { "jdbc.username", "jdbc.password" }; /** * 对关键的属性进行转换 */ @Override protected String convertProperty(String propertyName, String propertyValue) { if (isEncryptProp(propertyName)) { // 对已加密的字段进行解密工作 String decryptValue = DESUtil.getDecryptString(propertyValue); return decryptValue; } else { return propertyValue; } } /** * 该属性是否已加密 * * @param propertyName * @return */ private boolean isEncryptProp(String propertyName) { // 若等于需要加密的field,则进行加密 for (String encryptpropertyName : encryptPropNames) { if (encryptpropertyName.equals(propertyName)) return true; } return false; } }
MD5.java
package com.imooc.o2o.util; import java.security.MessageDigest; /** * MD5加密 */ public class MD5 { /** * 对传入的String进行MD5加密 * * @param s * @return */ public static final String getMd5(String s) { // 16进制数组 char hexDigits[] = { '5', '0', '5', '6', '2', '9', '6', '2', '5', 'q', 'b', 'l', 'e', 's', 's', 'y' }; try { char str[]; // 将传入的字符串转换成byte数组 byte strTemp[] = s.getBytes(); // 获取MD5加密对象 MessageDigest mdTemp = MessageDigest.getInstance("MD5"); // 传入需要加密的目标数组 mdTemp.update(strTemp); // 获取加密后的数组 byte md[] = mdTemp.digest(); int j = md.length; str = new char[j * 2]; int k = 0; // 将数组做位移 for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } // 转换成String并返回 return new String(str); } catch (Exception e) { return null; } } public static void main(String[] args) { System.out.println(MD5.getMd5("123456")); } }
4、分页
PageCalculator.java
package com.imooc.o2o.util; public class PageCalculator { public static int calculateRowIndex(int pageIndex, int pageSize) { return (pageIndex > 0) ? (pageIndex - 1) * pageSize : 0; } }
ProductServiceImpl.java
ackage com.imooc.o2o.service.impl; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.imooc.o2o.dao.ProductDao; import com.imooc.o2o.dao.ProductImgDao; import com.imooc.o2o.dto.ProductExecution; import com.imooc.o2o.entity.Product; @Service public class ProductServiceImpl implements ProductService { @Autowired private ProductDao productDao; @Autowired private ProductImgDao productImgDao; @Override public ProductExecution getProductList(Product productCondition, int pageIndex, int pageSize) { // 页码转换成数据库的行码,并调用dao层取回指定页码的商品列表 int rowIndex = PageCalculator.calculateRowIndex(pageIndex, pageSize); List<Product> productList = productDao.queryProductList(productCondition, rowIndex, pageSize); // 基于同样的查询条件返回该查询条件下的商品总数 int count = productDao.queryProductCount(productCondition); ProductExecution pe = new ProductExecution(); pe.setProductList(productList); pe.setCount(count); return pe; } @Override public Product getProductById(long productId) { return productDao.queryProductById(productId); } }
5、Echarts 图表
EchartSeries.java
package com.imooc.o2o.dto; import java.util.List; /** * 迎合echart里的series项 */ public class EchartSeries { private String name; private String type = "bar"; private List<Integer> data; public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Integer> getData() { return data; } public void setData(List<Integer> data) { this.data = data; } public String getType() { return type; } }
EchartXAxis.java
package com.imooc.o2o.dto; import java.util.HashSet; /** * 迎合echart里的xAxis项 */ public class EchartXAxis { private String type = "category"; //为了去重 private HashSet<String> data; public HashSet<String> getData() { return data; } public void setData(HashSet<String> data) { this.data = data; } public String getType() { return type; } }
UserProductManagementController.java
package com.imooc.o2o.web.shopadmin; import java.io.IOException; import java.net.URLDecoder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.fasterxml.jackson.databind.ObjectMapper; import com.imooc.o2o.dto.EchartSeries; import com.imooc.o2o.dto.EchartXAxis; import com.imooc.o2o.dto.ShopAuthMapExecution; import com.imooc.o2o.dto.UserAccessToken; import com.imooc.o2o.dto.UserProductMapExecution; import com.imooc.o2o.dto.WechatInfo; import com.imooc.o2o.entity.PersonInfo; import com.imooc.o2o.entity.Product; import com.imooc.o2o.entity.ProductSellDaily; import com.imooc.o2o.entity.Shop; import com.imooc.o2o.entity.ShopAuthMap; import com.imooc.o2o.entity.UserProductMap; import com.imooc.o2o.entity.WechatAuth; import com.imooc.o2o.enums.UserProductMapStateEnum; import com.imooc.o2o.service.ProductSellDailyService; import com.imooc.o2o.service.ProductService; import com.imooc.o2o.service.ShopAuthMapService; import com.imooc.o2o.service.UserProductMapService; import com.imooc.o2o.service.WechatAuthService; import com.imooc.o2o.util.HttpServletRequestUtil; import com.imooc.o2o.util.wechat.WechatUtil; @Controller @RequestMapping("/shopadmin") public class UserProductManagementController { @Autowired private UserProductMapService userProductMapService; @Autowired private ProductSellDailyService productSellDailyService; @Autowired private WechatAuthService wechatAuthService; @Autowired private ShopAuthMapService shopAuthMapService; @Autowired private ProductService productService; @RequestMapping(value = "/listuserproductmapsbyshop", method = RequestMethod.GET) @ResponseBody private Map<String, Object> listUserProductMapsByShop(HttpServletRequest request) { Map<String, Object> modelMap = new HashMap<String, Object>(); // 获取分页信息 int pageIndex = HttpServletRequestUtil.getInt(request, "pageIndex"); int pageSize = HttpServletRequestUtil.getInt(request, "pageSize"); // 获取当前的店铺信息 Shop currentShop = (Shop) request.getSession().getAttribute("currentShop"); // 空值校验,主要确保shopId不为空 if ((pageIndex > -1) && (pageSize > -1) && (currentShop != null) && (currentShop.getShopId() != null)) { // 添加查询条件 UserProductMap userProductMapCondition = new UserProductMap(); userProductMapCondition.setShop(currentShop); String productName = HttpServletRequestUtil.getString(request, "productName"); if (productName != null) { // 若前端想按照商品名模糊查询,则传入productName Product product = new Product(); product.setProductName(productName); userProductMapCondition.setProduct(product); } // 根据传入的查询条件获取该店铺的商品销售情况 UserProductMapExecution ue = userProductMapService.listUserProductMap(userProductMapCondition, pageIndex, pageSize); modelMap.put("userProductMapList", ue.getUserProductMapList()); modelMap.put("count", ue.getCount()); modelMap.put("success", true); } else { modelMap.put("success", false); modelMap.put("errMsg", "empty pageSize or pageIndex or shopId"); } return modelMap; } @RequestMapping(value = "/listproductselldailyinfobyshop", method = RequestMethod.GET) @ResponseBody private Map<String, Object> listProductSellDailyInfobyShop(HttpServletRequest request) throws ParseException { Map<String, Object> modelMap = new HashMap<String, Object>(); // 获取当前的店铺信息 Shop currentShop = (Shop) request.getSession().getAttribute("currentShop"); // 空值校验,主要确保shopId不为空 if ((currentShop != null) && (currentShop.getShopId() != null)) { // 添加查询条件 ProductSellDaily productSellDailyCondition = new ProductSellDaily(); productSellDailyCondition.setShop(currentShop); Calendar calendar = Calendar.getInstance(); // 获取昨天的日期 calendar.add(Calendar.DATE, -1); Date endTime = calendar.getTime(); // 获取七天前的日期 calendar.add(Calendar.DATE, -6); Date beginTime = calendar.getTime(); // 根据传入的查询条件获取该店铺的商品销售情况 List<ProductSellDaily> productSellDailyList = productSellDailyService .listProductSellDaily(productSellDailyCondition, beginTime, endTime); // 指定日期格式 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // 商品名列表,保证唯一性 LinkedHashSet<String> legendData = new LinkedHashSet<String>(); // x轴数据 LinkedHashSet<String> xData = new LinkedHashSet<String>(); // 定义series List<EchartSeries> series = new ArrayList<EchartSeries>(); // 日销量列表 List<Integer> totalList = new ArrayList<Integer>(); // 当前商品名,默认为空 String currentProductName = ""; for (int i = 0; i < productSellDailyList.size(); i++) { ProductSellDaily productSellDaily = productSellDailyList.get(i); // 自动去重 legendData.add(productSellDaily.getProduct().getProductName()); xData.add(sdf.format(productSellDaily.getCreateTime())); if (!currentProductName.equals(productSellDaily.getProduct().getProductName()) && !currentProductName.isEmpty()) { // 如果currentProductName不等于获取的商品名,或者已遍历到列表的末尾,且currentProductName不为空, // 则是遍历到下一个商品的日销量信息了, 将前一轮遍历的信息放入series当中, // 包括了商品名以及与商品对应的统计日期以及当日销量 EchartSeries es = new EchartSeries(); es.setName(currentProductName); es.setData(totalList.subList(0, totalList.size())); series.add(es); // 重置totalList totalList = new ArrayList<Integer>(); // 变换下currentProductId为当前的productId currentProductName = productSellDaily.getProduct().getProductName(); // 继续添加新的值 totalList.add(productSellDaily.getTotal()); } else { // 如果还是当前的productId则继续添加新值 totalList.add(productSellDaily.getTotal()); currentProductName = productSellDaily.getProduct().getProductName(); } // 队列之末,需要将最后的一个商品销量信息也添加上 if (i == productSellDailyList.size() - 1) { EchartSeries es = new EchartSeries(); es.setName(currentProductName); es.setData(totalList.subList(0, totalList.size())); series.add(es); } } modelMap.put("series", series); modelMap.put("legendData", legendData); // 拼接出xAxis List<EchartXAxis> xAxis = new ArrayList<EchartXAxis>(); EchartXAxis exa = new EchartXAxis(); exa.setData(xData); xAxis.add(exa); modelMap.put("xAxis", xAxis); modelMap.put("success", true); } else { modelMap.put("success", false); modelMap.put("errMsg", "empty shopId"); } return modelMap; } @RequestMapping(value = "/adduserproductmap", method = RequestMethod.GET) private String addUserProductMap(HttpServletRequest request, HttpServletResponse response) throws IOException { // 获取微信授权信息 WechatAuth auth = getOperatorInfo(request); if (auth != null) { PersonInfo operator = auth.getPersonInfo(); request.getSession().setAttribute("user", operator); // 获取二维码里state携带的content信息并解码 String qrCodeinfo = new String( URLDecoder.decode(HttpServletRequestUtil.getString(request, "state"), "UTF-8")); ObjectMapper mapper = new ObjectMapper(); WechatInfo wechatInfo = null; try { // 将解码后的内容用aaa去替换掉之前生成二维码的时候加入的aaa前缀,转换成WechatInfo实体类 wechatInfo = mapper.readValue(qrCodeinfo.replace("aaa", "\""), WechatInfo.class); } catch (Exception e) { return "shop/operationfail"; } // 校验二维码是否已经过期 if (!checkQRCodeInfo(wechatInfo)) { return "shop/operationfail"; } // 获取添加消费记录所需要的参数并组建成userproductmap实例 Long productId = wechatInfo.getProductId(); Long customerId = wechatInfo.getCustomerId(); UserProductMap userProductMap = compactUserProductMap4Add(customerId, productId, auth.getPersonInfo()); // 空值校验 if (userProductMap != null && customerId != -1) { try { if (!checkShopAuth(operator.getUserId(), userProductMap)) { return "shop/operationfail"; } // 添加消费记录 UserProductMapExecution se = userProductMapService.addUserProductMap(userProductMap); if (se.getState() == UserProductMapStateEnum.SUCCESS.getState()) { return "shop/operationsuccess"; } } catch (RuntimeException e) { return "shop/operationfail"; } } } return "shop/operationfail"; } /** * 根据code获取UserAccessToken,进而通过token里的openId获取微信用户信息 * * @param request * @return */ private WechatAuth getOperatorInfo(HttpServletRequest request) { String code = request.getParameter("code"); WechatAuth auth = null; if (null != code) { UserAccessToken token; try { token = WechatUtil.getUserAccessToken(code); String openId = token.getOpenId(); request.getSession().setAttribute("openId", openId); auth = wechatAuthService.getWechatAuthByOpenId(openId); } catch (IOException e) { e.printStackTrace(); } } return auth; } /** * 根据二维码携带的createTime判断其是否超过了10分钟,超过十分钟则认为过期 * * @param wechatInfo * @return */ private boolean checkQRCodeInfo(WechatInfo wechatInfo) { if (wechatInfo != null && wechatInfo.getProductId() != null && wechatInfo.getCustomerId() != null && wechatInfo.getCreateTime() != null) { long nowTime = System.currentTimeMillis(); if ((nowTime - wechatInfo.getCreateTime()) <= 600000) { return true; } else { return false; } } else { return false; } } /** * 根据传入的customerId, productId以及操作员信息组建用户消费记录 * * @param customerId * @param productId * @param operator * @return */ private UserProductMap compactUserProductMap4Add(Long customerId, Long productId, PersonInfo operator) { UserProductMap userProductMap = null; if (customerId != null && productId != null) { userProductMap = new UserProductMap(); PersonInfo customer = new PersonInfo(); customer.setUserId(customerId); // 主要为了获取商品积分 Product product = productService.getProductById(productId); userProductMap.setProduct(product); userProductMap.setShop(product.getShop()); userProductMap.setUser(customer); userProductMap.setPoint(product.getPoint()); userProductMap.setCreateTime(new Date()); userProductMap.setOperator(operator); } return userProductMap; } /** * 检查扫码的人员是否有操作权限 * * @param userId * @param userProductMap * @return */ private boolean checkShopAuth(long userId, UserProductMap userProductMap) { // 获取该店铺的所有授权信息 ShopAuthMapExecution shopAuthMapExecution = shopAuthMapService .listShopAuthMapByShopId(userProductMap.getShop().getShopId(), 1, 1000); for (ShopAuthMap shopAuthMap : shopAuthMapExecution.getShopAuthMapList()) { // 看看是否给过该人员进行授权 if (shopAuthMap.getEmployee().getUserId() == userId) { return true; } } return false; } }
6、主从数据库读写分离拦截器
DynamicDataSource.java
package com.imooc.o2o.dao.split; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource{ @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceHolder.getDbType(); } }
DynamicDataSourceHolder.java
package com.imooc.o2o.dao.split; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DynamicDataSourceHolder { private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceHolder.class); private static ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static final String DB_MASTER = "master"; public static final String DB_SLAVE = "slave"; public static String getDbType() { String db = contextHolder.get(); if (db == null) { db = DB_MASTER; } return db; } /** * 设置线程的dbType * * @param str */ public static void setDbType(String str) { logger.debug("所使用的数据源为:" + str); contextHolder.set(str); } /** * 清理连接类型 */ public static void clearDBType() { contextHolder.remove(); } }
DynamicDataSourceInterceptor.java
package com.imooc.o2o.dao.split; import java.util.Locale; import java.util.Properties; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.keygen.SelectKeyGenerator; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.support.TransactionSynchronizationManager; @Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }), @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) }) public class DynamicDataSourceInterceptor implements Interceptor { private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceInterceptor.class); private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*"; @Override public Object intercept(Invocation invocation) throws Throwable { boolean synchronizationActive = TransactionSynchronizationManager.isActualTransactionActive(); Object[] objects = invocation.getArgs(); MappedStatement ms = (MappedStatement) objects[0]; String lookupKey = DynamicDataSourceHolder.DB_MASTER; if (synchronizationActive != true) { // 读方法 if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) { // selectKey 为自增id查询主键(SELECT LAST_INSERT_ID())方法,使用主库 if (ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) { lookupKey = DynamicDataSourceHolder.DB_MASTER; } else { BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]); String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replaceAll("[\\t\\n\\r]", " "); if (sql.matches(REGEX)) { lookupKey = DynamicDataSourceHolder.DB_MASTER; } else { lookupKey = DynamicDataSourceHolder.DB_SLAVE; } } } } else { lookupKey = DynamicDataSourceHolder.DB_MASTER; } logger.debug("设置方法[{}] use [{}] Strategy, SqlCommanType [{}]..", ms.getId(), lookupKey, ms.getSqlCommandType().name()); DynamicDataSourceHolder.setDbType(lookupKey); return invocation.proceed(); } @Override public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties arg0) { // TODO Auto-generated method stub } }
7、全局异常处理器
GlobalExceptionHandler.java
package com.imooc.o2o.web.handler; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import com.imooc.o2o.exceptions.ProductOperationException; import com.imooc.o2o.exceptions.ShopOperationException; @ControllerAdvice public class GlobalExceptionHandler { private final static Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class); @ExceptionHandler(value = Exception.class) @ResponseBody public Map<String, Object> handle(Exception e) { Map<String, Object> modelMap = new HashMap<String, Object>(); modelMap.put("success", false); if (e instanceof ShopOperationException) { modelMap.put("errMsg", e.getMessage()); } else if (e instanceof ProductOperationException) { modelMap.put("errMsg", e.getMessage()); } else { LOG.error("系统出现异常", e.getMessage()); modelMap.put("errMsg", "未知错误,请联系工作人员进行解决"); } return modelMap; } }
这篇关于SpringBoot 中的微信登录、已有微信账号绑定用户名密码本地账号、加密解密字符串、分页、Echarts 图表、主从数据库读写分离拦截器、全局异常处理器的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-09聊聊如何通过arthas-tunnel-server来远程管理所有需要arthas监控的应用
- 2024-05-09log4j2这么配就对了
- 2024-05-09nginx修改Content-Type
- 2024-05-09Redis多数据源,看这篇就够了
- 2024-05-09Google Chrome驱动程序 124.0.6367.62(正式版本)去哪下载?
- 2024-05-09有没有大佬知道这种数据应该怎么抓取呀?
- 2024-05-09这种运行结果里的10.100000001,怎么能最快改成10.1?
- 2024-05-09企业src漏洞挖掘-有意思的命令执行
- 2024-05-08阿里云域名注册流程,分享给第一次购买域名的新手站长!
- 2024-05-082024年,行业变动下的程序员应该首先学习哪种编程语言?