1、支付宝沙箱
1.1 jar包 alipay-sdk
<!-- alipay-sdk --> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.10.90.ALL</version> </dependency>
1.2 信息配置类
1.2.1 配置信息
public class AlipayConfig { // 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号 public static String app_id = ""; // 商户应用私钥,您的PKCS8格式RSA2私钥 public static String merchant_private_key = ""; // 对应APPID下的支付宝公钥。 public static String alipay_public_key = ""; // 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 public static String notify_url = "http://localhost:8080(/项目名,没有的不需要)/Alipay/notify_url.do"; // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 public static String return_url = "http://localhost:8080(/项目名,没有的不需要)/Alipay/alipay_return.do"; // 签名方式 public static String sign_type = "RSA2"; // 字符编码格式 public static String charset = "UTF-8"; // 支付宝网关 public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do"; }
1.2.2配置信息来源
登录自己已经入驻的支付宝账号:https://open.alipay.com/develop/sandbox/app
1.2.2.1 APPID
1.2.2.2 公钥,私钥
1.3 支付控制层
/** * 支付宝支付 */ @Controller @RequestMapping("/Alipay") public class AlipayController { /** * 生成订单直接跳转支付宝付款 */ @RequestMapping("/to_alipay.do") public void toAlipay(HttpServletResponse response, HttpServletRequest request) throws Exception{ AlipayClient alipayClient = new DefaultAlipayClient( AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type); // 取购买人名称 String in_name = request.getParameter("in_name"); // 取手机号 String in_phone = request.getParameter("in_phone"); // 创建唯一订单号 int random = (int) (Math.random() * 10000); String dateStr = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); // 订单号拼接规则:手机号后四位+当前时间后四位+随机数四位数 String out_trade_no = in_phone.substring(7) + dateStr.substring(10) + random; // 拼接订单名称 String subject = in_name + "的订单"; // 取付款金额 String total_amount = request.getParameter("in_money"); // 设置请求参数 AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); alipayRequest.setReturnUrl(AlipayConfig.return_url);//支付成功响应后跳转地址 alipayRequest.setNotifyUrl(AlipayConfig.notify_url);//异步请求地址 /*FAST_INSTANT_TRADE_PAY 二维码瞬时支付 * out_trade_no 订单号 total_amount 订单金额 subject 订单名称 */ alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\"," + "\"total_amount\":\"" + total_amount + "\"," + "\"subject\":\"" + subject + "\"," + "\"body\":\"" + ""+ "\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}"); String result = "请求无响应"; // 请求 try { //通过阿里客户端,发送支付页面请求 result = alipayClient.pageExecute(alipayRequest).getBody(); response.setContentType("text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); response.getWriter().println(result); response.getWriter().flush(); } catch (AlipayApiException e) { e.printStackTrace(); } finally { response.getWriter().close(); } } /** * 支付成功后处理业务 */ @RequestMapping("/alipay_return.do") public String alipayReturn(HttpServletRequest request, Map<String, Object> map) throws Exception{ // 响应信息 String msg = ""; // 请在这里编写您的程序(以下代码仅作参考) if (verifyAlipayReturn(request)) {//验签成功后执行的自定义业务代码 // 商户订单号 String out_trade_no = new String(request.getParameter( "out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); // 支付宝交易号 String trade_no = new String(request.getParameter("trade_no") .getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); // 付款金额 String total_amount = new String(request.getParameter( "total_amount").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); msg = "支付宝交易号:" + trade_no + "<br/>商户订单号" + out_trade_no + "<br/>付款金额:" + total_amount; } else { msg = "验签/支付失败"; } map.put("msg", msg); return "forward:/success.jsp"; //支付完成后,跳转的页面 } /** * 支付宝异步通知 * @param request * @param response * @throws Exception */ @RequestMapping("/notify_url.do") public void alipayNotify(HttpServletRequest request,HttpServletResponse response) throws Exception { // ——请在这里编写您的程序(以下代码仅作参考)—— /* * 实际验证过程建议商户务必添加以下校验: 1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号, * 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), * 3、校验通知中的seller_id(或者seller_email) * 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email) * 4、验证app_id是否为该商户本身。 */ if (verifyAlipayReturn(request)) {// 验证成功 // 商户订单号 String out_trade_no = new String(request.getParameter( "out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); System.out.println(out_trade_no); // 支付宝交易号 String trade_no = new String(request.getParameter("trade_no") .getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); System.out.println(trade_no); // 交易状态 String trade_status = new String(request.getParameter( "trade_status").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); if (trade_status.equals("TRADE_FINISHED")) { // 判断该笔订单是否在商户网站中已经做过处理 // 如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序 // 如果有做过处理,不执行商户的业务程序 // 注意: // 退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 } else if (trade_status.equals("TRADE_SUCCESS")) { // 判断该笔订单是否在商户网站中已经做过处理 // 如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序 // 如果有做过处理,不执行商户的业务程序 // 注意: // 付款完成后,支付宝系统发送该交易状态通知 } } else {// 验证失败 response.setContentType("text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); response.getWriter().println("验签/支付失败!"); response.getWriter().flush(); response.getWriter().close(); // 调试用,写文本函数记录程序运行情况是否正常 // String sWord = AlipaySignature.getSignCheckContentV1(params); // AlipayConfig.logResult(sWord); } } /** * @author zhukang * @date 2021-04-23 * @return * @description 验证支付宝的反馈信息 */ private boolean verifyAlipayReturn(HttpServletRequest request) throws UnsupportedEncodingException { // 获取支付宝回调反馈的信息 Map<String, String> params = new HashMap<>(); Map<String, String[]> requestParams = request.getParameterMap(); for (Iterator<String> iter = requestParams.keySet().iterator(); iter .hasNext();) { String name = iter.next(); String[] values = requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } // 乱码解决,这段代码在出现乱码时使用 valueStr = new String(valueStr.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); params.put(name, valueStr); } boolean signVerified = false; try {// 调用SDK验证签名 signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); } catch (AlipayApiException e) { e.printStackTrace(); } return signVerified; } }
1.4 测试访问
直接访问 Alipay/to_alipay.do 这个请求即可(参数根据实际操作进行添加);
2、关键字过滤
2.1 关键字文件 sensitivewords.txt
小笨蛋 ...(根据实际需求添加关键字)
2.2 关键字工具类
2.2.1 关键字初始化
/** * Created On : 2022/7/26. * <p> * Author : zhukang * <p> * Description: 敏感词初始化类 */ public class SensitiveWordInit { // 敏感词集合 public static Map sensitiveWordMap; // 初始化敏感词 public Map initSensitiveWord(){ System.out.println("------- 系统启动,从文件中读取敏感字,存入sensitiveWordMap -------"); try { // 读取敏感词文件,将敏感词加入HashMap addSensitiveWordToHashMap(readSensitiveWordFile()); } catch (Exception e){ e.printStackTrace(); } return sensitiveWordMap; } /** * @author : zhukang * @date : 2022/7/26 * @param : [java.util.Set<java.lang.String>] * @return : java.util.Map * @description : 将HashSet中的敏感词,存入HashMap中 */ private void addSensitiveWordToHashMap(Set<String> wordSet) { // 初始化敏感词容器,减少扩容操作 sensitiveWordMap = new HashMap(wordSet.size()); for (String word : wordSet) { Map nowMap = sensitiveWordMap; for (int i = 0; i < word.length(); i++) { // 转换成char型 char keyChar = word.charAt(i); // 获取 Object tempMap = nowMap.get(keyChar); // 如果存在该key,直接赋值 if (tempMap != null) { // 一个一个放进Map中 nowMap = (Map) tempMap; } // 不存在则,则构建一个map,同时将isEnd设置为0,因为他不是最后一个 else { // 设置标志位,不是最后一个 Map<String, String> newMap = new HashMap<String, String>(); // 没有这个key,就把(isEnd,0) 放在Map中 newMap.put("isEnd", "0"); // 添加到集合 nowMap.put(keyChar, newMap); //指向当前map,继续遍历 nowMap = newMap; } // 最后一个 if (i == word.length() - 1) { nowMap.put("isEnd", "1"); } } } } /** * @author : zhukang * @date : 2022/7/26 * @param : [] * @return : java.util.Set<java.lang.String> * @description : 读取敏感词库文件,存入HashMap中 */ private Set<String> readSensitiveWordFile() { // 敏感词集合 Set<String> wordSet = null; //敏感词库 try ( // 获取输入流,读取resources目录下的static目录中的敏感词文件(一个敏感词一行) InputStream inputStream = new ClassPathResource("sensitivewords.txt").getInputStream(); // 读取文件输入流 InputStreamReader read = new InputStreamReader(inputStream, "UTF-8"); // 高效读取 BufferedReader br = new BufferedReader(read); ) { // 创建set集合,存储读取的敏感字 wordSet = new HashSet<>(); //手动 添加词语 // wordSet.add("笨蛋"); // wordSet.add("傻瓜"); // 读取文件,将文件内容放入到set中 String txt = null; while ((txt = br.readLine()) != null) { wordSet.add(txt); } } catch (Exception e) { e.printStackTrace(); } // 返回敏感字集合 return wordSet; } }
2.2.2 关键字过滤类
/** * Created On : 2022/7/26. * <p> * Author : zhukang * <p> * Description: 敏感字过滤工具类 */ @Component public class SensitiveWordFilterUtil { // 最小匹配规则 public static final int MIN_MATCH_TYPE = 1; // 最大匹配规则 public static final int MAX_MATCH_TYPE = 2; // 敏感词集合 public static Map sensitiveWordMap; // 应用启动后,创建实例对象,自动执行此方法 @PostConstruct public void init() { // 从数据库查询敏感词,转换为set集合 将敏感词库加入到HashMap中,确定有穷自动机DFA sensitiveWordMap = new com.kgc.weddingshop.utils.SensitiveWordInit().initSensitiveWord(); System.out.println("------ " + sensitiveWordMap + "------"); } /** * @param : [java.lang.String] * @return : boolean true 包含;false 不包含 * @author : zhukang * @date : 2022/7/26 * @description : 是否包含敏感词(默认按最小匹配规则来,只要有敏感词就ok),最小匹配规则 */ public boolean isContainSensitiveWordMin(String txt) { return isContainSensitiveWord(txt, MIN_MATCH_TYPE); } /** * @param : [java.lang.String, int] * @return : boolean * @author : zhukang * @date : 2022/7/26 * @description : 是否包含敏感词(默认按指定匹配规则来,只要有敏感词就ok) * 如果敏感词库为: * * 傻叉 * * 傻叉人 * * 大傻叉 * * 初始化之后为:{傻={叉={人={isEnd=1}, isEnd=1}, isEnd=0}, 大={傻={叉={isEnd=1}, isEnd=0}, isEnd=0}} * * 1、按最小规则匹配, 匹配 傻叉 的时候,匹配到叉,就为最后一个了 直接break。如果输入的敏感词是傻叉人,命中的只是傻叉,而不是傻叉人 * * 2、按最大规则匹配, 匹配 傻叉 的时候,匹配到叉,已经为最后一个,但是按照最大规则,会继续匹配人,命中的是傻叉人 * * 3、如果关键词是傻叉猫,两种匹配规则都会匹配到傻叉,会命中,如果输入的是傻叉人猫,按最小规则匹配是傻叉,按最大规则匹配傻叉人,只是匹配敏感词不同 */ public boolean isContainSensitiveWord(String txt, int matchType) { if (txt == null || "".equals(txt)) { return false; } for (int i = 0; i < txt.length(); i++) { int matchFlag = this.checkSensitiveWords(txt, i, matchType); if (matchFlag > 0) { return true; } } return false; } /** * @param : 待判断文本 起始位置 匹配类型: 1 最小匹配原则;2 最大匹配原则 * @return : int 大于0表示包含敏感词且表示敏感词匹配长度,否则不包含 * @author : zhukang * @date : 2022/7/26 * @description : 校验是否包含敏感词 */ private static int checkSensitiveWords(String txt, int beginIndex, int matchType) { // 敏感词结束标识位:用于敏感词只有1位的情况 boolean flag = false; // 匹配标识数默认为0 int matchFlag = 0; // 从内存中,获取敏感词库 Map nowMap = sensitiveWordMap; for (int i = beginIndex; i < txt.length(); i++) { // 获取第一个字符 char word = txt.charAt(i); // 获取指定key,判断当前字符是不是一个敏感词的开头 nowMap = (Map) nowMap.get(word); // 不存在,直接返回 if (nowMap == null) { break; } // 根据排列组合的匹配,如果出现在敏感词库中,即找到相应key,匹配标识+1 matchFlag++; // 如果已经匹配到词库中完整的敏感词, 改匹配结束标识,并根据匹配规则判断,是否继续 if ("1".equals(nowMap.get("isEnd"))) { // 结束标志位为true,已经命中到了一个完整敏感词 flag = true; // 最小规则,直接返回, 最大规则还需继续查找 if (matchType == MIN_MATCH_TYPE) { break; } } } // 长度必须大于等于1,为词,敏感词只有1个字的情况 if (matchFlag < 2 || !flag) { matchFlag = 0; } return matchFlag; } /** * @param : txt 待判断文本 * @return : 匹配类型: 1 最小匹配原则;2 最大匹配原则 * @author : zhukang * @date : 2022/7/26 * @description : 获取匹配的敏感词 */ public Set<String> getSensitiveWords(String txt, Integer matchType) { Set<String> sensitiveWords = new HashSet<>(); for (int i = 0; i < txt.length(); i++) { Integer length = checkSensitiveWords(txt, i, matchType); if (length > 0) { sensitiveWords.add(txt.substring(i, i + length)); // 循环i会+1,所以需-1 i = i + length - 1; } } return sensitiveWords; } /** * @param : txt,文本 matchType 匹配类型: 1 最小匹配原则;2 最大匹配原则 * @return : 替换字符 * @author : zhukang * @date : 2022/7/26 * @description : 替换敏感词 */ public String replaceSensitiveWords(String txt, Integer matchType, String replaceStr) { if (txt == null || "".equals(txt)) { return txt; } // 获取所有敏感词 Set<String> sensitiveWords = getSensitiveWords(txt, matchType); Iterator<String> iterator = sensitiveWords.iterator(); String replaceString = ""; while (iterator.hasNext()) { String sWord = iterator.next(); replaceString = getReplaceString(replaceStr, sWord.length()); txt = txt.replaceAll(sWord, replaceString); } return txt; } /** * @param : replaceStr 替换字符 * @return : 敏感字长度 * @author : zhukang * @date : 2022/7/26 * @description : 替换为指定字符,没有指定替换字符,默认* */ private static String getReplaceString(String replaceStr, Integer length) { // 指定替换字符为* if (replaceStr == null) { replaceStr = "*"; } // 可变字符串对象 StringBuffer replaceString = new StringBuffer(); // 循环遍历,替换内容 for (int i = 0; i < length; i++) { replaceString.append(replaceStr); } return replaceString.toString(); } }
2.3 关键字过滤 控制层
/** * Created On : 2022/7/26. * <p> * Author : zhukang * <p> * Description: 敏感词测试入口 */ @Controller public class SensitiveWordController { @Autowired private SensitiveWordFilterUtil sensitiveWordFilterUtil; /** * @author : zhukang * @date : 2022/5/17 * @param : [java.lang.String] * @return : com.kgc.sbt.util.RequestResult<java.lang.String> * @description : 测试搜索中的敏感词,并指定规则 */ @RequestMapping(value = "/testSensitiveWord", produces = {"application/json;charset=utf-8"}) @ResponseBody public String testSensitiveWord(@RequestParam String searchKey, @RequestParam int matchType){ // 校验搜索关键字中,是否包含敏感词,如果包含,提示错误 if(sensitiveWordFilterUtil.isContainSensitiveWord(searchKey, matchType)){ System.out.println(String.format("------ 命中敏感词,搜索关键字:%s ------", searchKey)); System.out.println(String.format("------ 命中敏感字为:%s ------", sensitiveWordFilterUtil.getSensitiveWords(searchKey, matchType))); return "搜索失败,命中敏感词!"; } return "搜索成功!"; } }
3、SSM项目 知识点
3.1 SpringMvc 重定向后,中文乱码
3.1.1 RedirectAttributes attributes
将参数,放入RedirectAttributes 中,在重定向的时候,会自动拼接参数,并且不会乱码;
@RequestMapping("/test") public String delAllViewHistory(//attributes 请求,自动拼接参数 RedirectAttributes attributes){ attributes.addAttribute("test","测试"); return "redirect:/viewHistory/viewViewHistoryList"; }
3.2 location.href 会暴露参数问题
解决方法:创建一个form表单,进行post方法提交;
//创建一个 form 表单,并提交 var form = $("<form>"); form.attr("style","display:none"); form.attr("target",""); form.attr("method","post"); //请求地址 form.attr("action","${pageContext.request.contextPath}/user/modUserInfo"); //请求参数 var input1 = $("<input>"); var input2 = $("<input>"); input1.attr("type","hidden"); input1.attr("name","uid"); input1.attr("value","${sessionScope.loginUser.uid}"); input2.attr("type","hidden"); input2.attr("name","password"); input2.attr("value",$password.val()); //在body标签中追加form 表单 $("body").append(form); form.append(input1); form.append(input2); //表单体提交 form.submit(); //移除表达 form.remove();
3.3 mysql 查询日期操作 ,今天,本周,本月,本季度,今年
-- 今天 select to_days(now()) -- 738788 天 -- 本周 select yearweek(now(),1) -- 202239 周, 第二个参数,是因为,中国人喜欢将周一当作一周的第一天 -- 本月 select date_format(now(),'%Y%m') -- 202209 月 -- 本季度 select quarter(now()) -- 3 季度 quarter -- 今年 select year(now()) -- 2022 年 year -- 日期格式化 select DATE_FORMAT(now(),'%Y-%m-%d')
3.4 头像点击上传
3.4.1 jsp
3.4.1.1 头像修改表单
<%-- 头像修改 form start --%> <form action="${pageContext.request.contextPath}/user/headImg" method="post" enctype="multipart/form-data" id="userHeadImgForm"> <input type="file" name="userHeaderImg" id="userHeaderPic" style="display: none"/> </form> <%-- 头像修改 form end --%>
3.4.1.2 头像展示
<%-- 头像 展示 start --%> <h3> <a style="margin-left: 130px"> <img style=" width: 50px;height: 50px" id="userHeaderImg" src="${pageContext.request.contextPath}/${sessionScope.loginUser.uhead}"/> </a> </h3> <%-- 头像 展示 end --%>
3.4.1.3 点击头像触发 图片选择input 头像选择
//============ 头像更换 start ================ $(function (){ // 点击头像图片,触发文件预点击 $("#userHeaderImg").click(function (){ $("#userHeaderPic").click(); }); // 当文件域内容改变,实现头像预览 $("#userHeaderPic").change(function () { // 获取上传文件对象 var file = $(this)[0].files[0]; // 判断类型 var imageType = /^image\//; if (file === undefined || !imageType.test(file.type)) { alert("请选择图片!"); return; } // 判断大小 if (file.size > 512000) { alert("图片大小不能超过500K!"); return; } // 读取文件URL,预览文件 var reader = new FileReader(); // 读取文件 reader.readAsDataURL(file); // 阅读文件完成后触发的事件 reader.onload = function () { // 读取的URL结果:this.result $("#userHeaderImg").attr("src", this.result); // alert("图片上传"); $("#userHeadImgForm").submit(); }; // TODO 还可以不预览,直接异步ajax发送请求到后台,上传文件,然后返回页面显示头像,并将图片的路径防止页面隐藏域,提交表单记录头像的地址 }); //============ 头像更换 end ================
3.4.2 控制层
1.获取头像文件流,并保存图片;
2.将图片地址保存到用户的头像中;
3.刷新session中的用户信息;
@RequestMapping("/headImg") public String headImg(HttpSession session, @RequestParam("userHeaderImg") MultipartFile multipartFile, Map<String, String> map) throws IOException { // 获取上传的头像文件名称 String targetFileName = multipartFile.getOriginalFilename(); System.out.println("------ 上传文件名:" + targetFileName + " ------"); // 重新定义新的文件名,要保留上传文件的类型 targetFileName = UUID.randomUUID().toString().substring(0, 8) + targetFileName.substring(targetFileName.indexOf(".")); System.out.println("------ 新的文件名:" + targetFileName + " ------"); // 上传文件,要保存服务器上的真实路径中,idea项目发布,默认不会放到目标tomcat中,放在本地项目的target目录中 String realFilePath = session.getServletContext().getRealPath("img/about"); System.out.println("------ 服务器真实路径:" + realFilePath + " ------"); // 目标文件目录可能不存在,不能人为干预,必须程序主动处理 File targetFilePath = new File(realFilePath); if(!targetFilePath.exists()){ // 目标不存在,主动创建 if(targetFilePath.mkdirs()){ System.out.println("------ 上传目录创建成功 ------"); } } // 创建目标文件对象 File targetFile = new File(targetFilePath + "/" + targetFileName); // 文件上传到服务器,只需要一步,组件自动支持功能 multipartFile.transferTo(targetFile); // 数据库存入头像信息 targetFileName = "img/about/"+targetFileName; //获取当前登录用户对象 User loginUser = (User)session.getAttribute(CommConstant.SYS_SESSION_LOGINUSER); //换头像 loginUser.setUhead(targetFileName); //重置 session 中的 用户对象 session.setAttribute(CommConstant.SYS_SESSION_LOGINUSER,loginUser); // 调用 修改用户信息 方法,修改用户信息 User userForm = new User(); userForm.setUid(loginUser.getUid()); userForm.setUhead(loginUser.getUhead()); userService.modUserInfo(userForm); //放入修改成功提示信息,可以不提示(操作过后感觉,不返回效果好些,看时机需求) map.put("modUserInfoMsg","用户头像修改成功!"); return "/personCenter"; }
标签:
留言评论