1. 搭建用户模块中心环境
1.1 后端环境搭建
1.1.1 步骤分析
- 创建模块
- 导包
- 配置
- 入口类
- 日志
- 网关
- swagger
- 生成代码
- 分页插件配置
- 测试
1.1.2 步骤实现
- 创建模块
- hrm_parent
- hrm_user_parent
- hrm_user_common
- hrm_user_client
- hrm_user_service
- hrm_user_parent
- 导包
- common
<!--web场景-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--测试场景-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--公共的工具-->
<dependency>
<groupId>cn.wangningbo.hrm</groupId>
<artifactId>hrm_basic_util</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--不能全部引入mybatis-plus-boot-starter,这是要做数据库操作,这里是不需要的,只需引入核心包解决错误而已-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>2.2.0</version>
</dependency>
- client
<!--引入公共依赖-->
<dependency>
<groupId>cn.wangningbo.hrm</groupId>
<artifactId>hrm_user_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--客户端feign支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- service
<!--引入公共依赖-->
<dependency>
<groupId>cn.wangningbo.hrm</groupId>
<artifactId>hrm_user_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Eureka 客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--引入swagger支持-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--配置中心支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--mybatis-plus支持-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--数据库支持-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--调用redis-->
<dependency>
<groupId>cn.wangningbo.hrm</groupId>
<artifactId>hrm_basic_redis_client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--发送短信所需的荣联云通讯的jar//注:这个maven是我手动打的jar包安装成pom依赖-->
<dependency>
<groupId>com.cloopen</groupId>
<artifactId>CCP_REST_SMS_SDK_JAVA</artifactId>
<version>v2.6.3r</version>
</dependency>
- 配置
server:
port: 9008
spring:
application:
name: hrm-user
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/hrm_user
username: root
password: apy06942
mybatis-plus:
mapper-locations: classpath:cn/itsource/hrm/mapper/*Mapper.xml
type-aliases-package: cn.wangningbo.hrm.domain,cn.wangningbo.hrm.query
sms:
account:
sid: 8a216da86d05dc0b016d33eb2395159a
token: 2d17dec7382e4c19a8447b47ed830625
appId: 8a216da86d05dc0b016d33eb23e815a0
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
prefer-ip-address: true
- 入口类
package cn.wangningbo.hrm;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@MapperScan("cn.wangningbo.hrm.mapper")
public class User9008Application {
public static void main(String[] args) {
SpringApplication.run(User9008Application.class, args);
}
}
- 日志
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<!-- 定义日志的根目录 -->
<property name="LOG_HOME" value="/hrm/" />
<!-- 定义日志文件名称 -->
<property name="appName" value="hrm-user"></property>
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--
日志输出格式:
%d表示日期时间,
%thread表示线程名,
%-5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符
-->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</layout>
</appender>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 指定日志文件的名称
/hrm/hrm-course/hrm-course.log
-->
<file>${LOG_HOME}/${appName}/${appName}.log</file>
<!--
当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
%i:当文件大小超过maxFileSize时,按照i进行文件滚动
-->
<fileNamePattern>${LOG_HOME}/${appName}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!--
可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
那些为了归档而创建的目录也会被删除。
-->
<MaxHistory>365</MaxHistory>
<!--
当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出格式: -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
</layout>
</appender>
<!--
logger主要用于存放日志对象,也可以定义日志类型、级别
name:表示匹配的logger类型前缀,也就是包的前半部分
level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
false:表示只用当前logger的appender-ref,true:
表示当前logger的appender-ref和rootLogger的appender-ref都有效
-->
<!-- hibernate logger -->
<logger name="cn.wangningbo" level="debug" />
<!-- Spring framework logger -->
<logger name="org.springframework" level="debug" additivity="false"></logger>
<!--
root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
-->
<root level="info">
<appender-ref ref="stdout" />
<appender-ref ref="appLogAppender" />
</root>
</configuration>
- 网关
在网关的配置文件里面配置
zuul:
routes:
pageAgent.serviceId: hrm-page-agent # 服务名
pageAgent.path: /pageAgent/** # 把pageAgent打头的所有请求都转发给hrm-page-agent
- swagger
自身配置
package cn.wangningbo.hrm.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("用户中心系统")
.description("用户中心接口文档说明")
.termsOfServiceUrl("http://www.wangningbo.cn")
.contact(new Contact("wangningbo", "", "wang_ning_bo163@163.com"))
.version("1.0")
.build();
}
}
网关配置
resources.add(swaggerResource("用户中心系统", "/services/user/v2/api-docs", "2.0"));
- 生成代码
配置文件
#代码放到哪儿
ServiceOutputDir=D:\\Java20190329\\workspace\\java6webSpringCloud\\hrm_parent\\hrm_user_parent\\hrm_user_service\\src\\main\\java
#mapper.xml SQL映射文件目录
ServiceOutputDirXml=D:\\Java20190329\\workspace\\java6webSpringCloud\\hrm_parent\\hrm_user_parent\\hrm_user_service\\src\\main\\resources
#接口路径
CommonOutputDirBase=D:\\Java20190329\\workspace\\java6webSpringCloud\\hrm_parent\\hrm_user_parent\\hrm_user_common\\src\\main\\java
ClientOutputDirBase=D:\\Java20190329\\workspace\\java6webSpringCloud\\hrm_parent\\hrm_user_parent\\hrm_user_client\\src\\main\\java
#设置作者
author=wangningbo
#自定义包路径
parent=cn.wangningbo.hrm
#数据库连接信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///hrm_user
jdbc.user=root
jdbc.pwd=apy06942
生成代码
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.*;
/**
* 新的
* Created by wangningbo on 2019/08/30
*/
public class GenteratorCode {
public static void main(String[] args) throws InterruptedException {
//用来获取Mybatis-Plus.properties文件的配置信息
ResourceBundle rb = ResourceBundle.getBundle("mybatisplus-user");
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir(rb.getString("ServiceOutputDir"));
gc.setFileOverride(true);
gc.setActiveRecord(true);// 开启 activeRecord 模式
gc.setEnableCache(false);// XML 二级缓存
gc.setBaseResultMap(true);// XML ResultMap
gc.setBaseColumnList(false);// XML columList
gc.setAuthor(rb.getString("author"));
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setDbType(DbType.MYSQL);
dsc.setTypeConvert(new MySqlTypeConvert());
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername(rb.getString("jdbc.user"));
dsc.setPassword(rb.getString("jdbc.pwd"));
dsc.setUrl(rb.getString("jdbc.url"));
mpg.setDataSource(dsc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setTablePrefix(new String[] { "t_" });// 此处可以修改为您的表前缀
strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
strategy.setInclude(new String[]{"t_sso","t_vip_address","t_vip_base","t_vip_course_collect",
"t_vip_course_view","t_vip_grow_log","t_vip_login_log","t_vip_msg","t_vip_realinfo"}); // 需要生成的表
mpg.setStrategy(strategy);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent(rb.getString("parent"));
pc.setController("web.controller");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setEntity("domain");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 注入自定义配置,可以在 VM 中使用 cfg.abc 【可无】
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-rb");
this.setMap(map);
}
};
List<FileOutConfig> focList = new ArrayList<FileOutConfig>();
// 调整 xml 生成目录演示
focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return rb.getString("ServiceOutputDirXml")+ "/cn/wangningbo/hrm/mapper/" + tableInfo.getEntityName() + "Mapper.xml";
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 调整 domain 生成目录演示
focList.add(new FileOutConfig("/templates/controller.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return rb.getString("ServiceOutputDir")+ "/cn/wangningbo/hrm/web/controller/" + tableInfo.getEntityName() + "Controller.java";
}
});
//=====================接口里面存放==================================//
// 调整 domain 生成目录演示
focList.add(new FileOutConfig("/templates/entity.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return rb.getString("CommonOutputDirBase")+ "/cn/wangningbo/hrm/domain/" + tableInfo.getEntityName() + ".java";
}
});
// 调整 query 生成目录演示
focList.add(new FileOutConfig("/templates/query.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return rb.getString("CommonOutputDirBase")+ "/cn/wangningbo/hrm/query/" + tableInfo.getEntityName() + "Query.java";
}
});
// 调整 client 生成目录演示
focList.add(new FileOutConfig("/templates/client.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return rb.getString("ClientOutputDirBase")+ "/cn/wangningbo/hrm/client/" + tableInfo.getEntityName() + "Client.java";
}
});
// 调整 ClientHystrixFallbackFactory 生成目录演示
focList.add(new FileOutConfig("/templates/ClientHystrixFallbackFactory.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return rb.getString("ClientOutputDirBase")+ "/cn/wangningbo/hrm/client/" + tableInfo.getEntityName() + "ClientHystrixFallbackFactory.java";
}
});
//=====================接口里面存放==================================//
// 自定义模板配置,可以 copy 源码 mybatis-plus/src/main/resources/templates 下面内容修改,
// 放置自己项目的 src/main/resources/templates 目录下, 默认名称一下可以不配置,也可以自定义模板名称
TemplateConfig tc = new TemplateConfig();
tc.setService("/templates/service.java.vm");
tc.setServiceImpl("/templates/serviceImpl.java.vm");
tc.setEntity(null);
tc.setMapper("/templates/mapper.java.vm");
tc.setController(null);
tc.setXml(null);
// 如上任何一个模块如果设置 空 OR Null 将不生成该模块。
mpg.setTemplate(tc);
// 执行生成
mpg.execute();
}
}
- 分页插件配置
package cn.wangningbo.hrm.config;
import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
//Spring boot方式:配置分页插件
@EnableTransactionManagement
@Configuration
@MapperScan("cn.wangningbo.hrm.mapper")
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
-
测试
postman测试
1.2 前端环境搭建
- 创建模块
- 拷贝html
- 运行
live-server --port=6003
- 后端配置跨域放行
在网关那里配置一下
config.addAllowedOrigin("http://127.0.0.1:6003");
config.addAllowedOrigin("http://localhost:6003");
完整代码
package cn.wangningbo.hrm.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
//跨域处理
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1.添加CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//1) 允许的域,不要写*,否则cookie就无法使用了
config.addAllowedOrigin("http://127.0.0.1:6001");
config.addAllowedOrigin("http://localhost:6001");
config.addAllowedOrigin("http://127.0.0.1:6003");
config.addAllowedOrigin("http://localhost:6003");
//2) 是否发送Cookie信息
config.setAllowCredentials(true);
//3) 允许的请求方式
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
// 4)允许的头信息
config.addAllowedHeader("*");
//2.添加映射路径,我们拦截一切请求
UrlBasedCorsConfigurationSource configSource = new
UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
//3.返回新的CorsFilter.
return new CorsFilter(configSource);
}
}
2. 相关技术的实现
2.1 随机数
用于获取验证码等
public static String getRandomString(int length) {
String str = "0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(10);
sb.append(str.charAt(number));
}
return sb.toString();
}
2.2 redis存带过期时间的数据
RedisClient新增一个方法
@PostMapping("/timeout")
void set(@RequestParam("key")String key, @RequestParam("value")String value,@RequestParam("timeout")int timeout);
RedisClientFallbackFactory那里生成一下,暂时不做处理
@Override
public void set(String key, String value, int timeout) {
}
RedisController
/**
* 设置key-value并设置过期时间
*
* @param key
* @param value
* @param timeout
*/
@PostMapping("/timeout")
void set(@RequestParam("key") String key, @RequestParam("value") String value, @RequestParam("timeout") int timeout) {
//获取连接
Jedis jedis = RedisUtils.INSTANCE.getSource();
// 存储key-value,并设置过期时间
jedis.setex(key, timeout, value);
//关闭连接
jedis.close();
}
2.3 图形验证码
验证码有很多种,我这选择:随机数字+字母
因为这种简单免费,但是有点弱!比较NB的验证码都收费,所以没有选择!
2.3.1 非前后端分离
- 后台生成验证码放入session
- 后台把验证码写入图片,把图片以流的方式返回给用户
- 用户根据图片输入验证码,后台获取到后从seession获取出来做比对.
2.3.2 前后端分离
前后端分离-session不能使用
前端的图片标签<img src="">的src属性,不仅仅可以存放地址,也可以存放base64来显示图片
2.3.2.1 方案说明
html5有两种在浏览器的存放数据机制
- localStorage:持久化,不删除一直存在
- sessionStorage:前端session
验证码流程:
- 用户使用uuid作为验证码的key,并把uuid生成的key存放在localStorage
- 前端发送请求,通过key获取验证码
- 后端生成6位验证码,存放到redis,key为前端传过来的key,value为生成的6位验证码进行base64加密后的结果,过期时间设置为5分钟。把验证码写入图片,把图片使用base64编码返回
- 前端展示后端返回的base64图片验证码
- 用户输入验证码,发送请求,把key和用户输入的验证码传递到后端
- 后端把用户传入的验证码通过base64加密,后端根据前端传过来的key从redis中获取验证码,比对是否与用户传过来的验证码相同。
2.3.2.1 方案实现
- 前端
methods: {
//构造请求,获取验证码
getValidateCode() {
//1 构造uuid
var codeUuid = localStorage.getItem("codeUuid");
let uuid = null;
if (codeUuid) {
uuid = codeUuid;
} else {
uuid = this.uuid();
localStorage.setItem("codeUuid", uuid);
}
//2 获取验证码
this.$http.get("/user/imgCode?uuid=" + uuid)
.then(result => {
this.base64Code = result.data;
});
;
},
// 构造前端的uuid
uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
},
mounted() {
//初始化验证码
this.getValidateCode();
}
- 后端的controller
package cn.wangningbo.hrm.web.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 图形验证码
*/
@RestController
@RequestMapping("/imgCode")
public class ImageValidateCodeController {
@Autowired
private ImageValidateCodeService service;
// 获取图片验证码
@GetMapping
public String getCode(String uuid) {
return service.getCode(uuid);
}
}
- IImageValidateCodeService
package cn.wangningbo.hrm.service;
public interface IImageValidateCodeService{
String getCode(String uuid);
}
- ImageValidateCodeImpl
package cn.wangningbo.hrm.service.impl;
import cn.wangningbo.hrm.client.RedisClient;
import cn.wangningbo.hrm.service.IImageValidateCodeService;
import cn.wangningbo.hrm.util.VerifyCodeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Encoder;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@Service
public class ImageValidateCodeImpl implements IImageValidateCodeService {
@Autowired
private RedisClient redisClient;
@Override
public String getCode(String uuid) {
// 生成6位验证码,并将所有英文字母转换为小写字母
String code = VerifyCodeUtils.generateVerifyCode(6).toLowerCase();
// 把验证码存放到redis,key使用前端传过来的uuid,并设置过期时间为5分钟
redisClient.set(uuid, code, 300);
// 输出到图片
ByteArrayOutputStream data = new ByteArrayOutputStream();
try {
// 设置图片的长度,宽度,图片显示的内容
VerifyCodeUtils.outputImage(100, 30, data, code);
} catch (IOException e) {
e.printStackTrace();
}
// 把图片加密返回
return new BASE64Encoder().encode(data.toByteArray());
}
}
- 刷新前端,即可看到验证码已出现!
2.4 短信验证码
- 导包(已经导入了,自己maven安装的那个)
- 发送短信的工具
package cn.wangningbo.hrm.util;
import com.cloopen.rest.sdk.CCPRestSmsSDK;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Set;
@Component
@ConfigurationProperties(prefix = "sms.account")
public class SmsHelper {
private String sid;
private String token;
private String appId;
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public void sendSms(String phones,String templateId ,String[] params){
System.out.println(sid);
System.out.println(token);
System.out.println(appId);
HashMap<String, Object> result = null;
CCPRestSmsSDK restAPI = new CCPRestSmsSDK();
restAPI.init("app.cloopen.com", "8883");// 初始化服务器地址和端口,格式如下,服务器地址不需要写https://
restAPI.setAccount(sid,token);// 初始化主帐号和主帐号TOKEN
restAPI.setAppId(appId);// 初始化应用ID
result = restAPI.sendTemplateSMS(phones,templateId ,params);
System.out.println("SDKTestSendTemplateSMS result=" + result);
if("000000".equals(result.get("statusCode"))){
//正常返回输出data包体信息(map)
HashMap<String,Object> data = (HashMap<String, Object>) result.get("data");
Set<String> keySet = data.keySet();
for(String key:keySet){
Object object = data.get(key);
System.out.println(key +" = "+object);
}
}else{
//异常返回输出错误码和错误信息
System.out.println("错误码=" + result.get("statusCode") +" 错误信息= "+result.get("statusMsg"));
}
}
}
- 测试
package cn.wangningbo.hrm.util;
import cn.wangningbo.hrm.User9008Application;
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;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = User9008Application.class)
public class SmsHelperTest {
@Autowired
private SmsHelper smsHelper;
@Test
public void sendSms() {
smsHelper.sendSms("17752526745","1",new String[]{"8888","5"});
}
}
网友评论