短信验证码
可以在利用中国网建的接口
网址:
http://www.smschinese.cn/
1.注册
demo学习
浏览器模拟
https://blog.csdn.net/sswltt/article/details/80415284
后台实现api封装
写短信发送接口-commonservice
Feign
@FeignClient(value = "HRM-COMMON", fallbackFactory = SmsClientFallbackFactory.class )//服务提供
@RequestMapping("/sms")
public interface SmsClient {
//feign如果要传递Map list等需要特殊处理
@PostMapping
AjaxResult send(@RequestParam String params);
}
短信接口常用类
public interface SmsContants {
//注册名
public static final String SMS_UID="中国网建的注册名";
//密钥
public static final String SMS_KEY="密钥";
}
service
//参数可变
@Override
public AjaxResult sendSmsCode(Map<String, String> params) {
PostMethod post = null;
try {
HttpClient client = new HttpClient();
post = new PostMethod("http://utf8.api.smschinese.cn");
post.addRequestHeader("Content-Type",
"application/x-www-form-urlencoded;charset=utf-8");//在头文件中设置转码
//动态参数 //map===>list====>array
List<NameValuePair> datas = new ArrayList<>();
for (String key : params.keySet()) {
datas.add( new NameValuePair(key, params.get(key) ));
}
NameValuePair[] data = datas.toArray(new NameValuePair[]{});
post.setRequestBody(data);
client.executeMethod(post);
Header[] headers = post.getResponseHeaders();
int statusCode = post.getStatusCode(); //400 404 200 500
String result = new String(post.getResponseBodyAsString().getBytes("utf-8"));
System.out.println("statusCode:"+statusCode);
System.out.println(result); //打印返回消息状态
if (statusCode==200){
//对各种错误码进行处理
return AjaxResult.me().setMessage(result);
}else{
return AjaxResult.me().setSuccess(false).setMessage(result);
}
}catch (Exception e){
e.printStackTrace();
}
finally {
if (post != null) {
post.releaseConnection();
}
}
return null;
}
调用短信服务
@Override
public AjaxResult sendSmsCode(Map<String, String> params) {
// 一 接收参数
//手机号
String mobile = params.get("mobile");
//图片验证码
String imageCode = params.get("imageCode");
//图片验证码的key
String imageCodeKey = params.get("imageCodeKey");
// 二 做检验
//2.1 图片验证 通过key从redis获取存放验证码和输入的进行对比
String imgCodeforReids = redisClient.get(imageCodeKey);
if (imgCodeforReids==null || !imageCode.equals(imgCodeforReids)){
return AjaxResult.me().setSuccess(false).setMessage("图片验证码不正确!");
}
//2.2 手机号的验证-判null,是否已经注册了
if (mobile==null){
return AjaxResult.me().setSuccess(false).setMessage("请输入正确的手机号码");
}
List<Sso> ssos = ssoMapper
.selectList(new EntityWrapper<Sso>().eq("phone", mobile));
if (ssos!=null && ssos.size()>0){
return AjaxResult.me().setSuccess(false).setMessage("你的手机号已经注册了,可以直接使用!");
}
// 三 发送短信验证码
return sendSmsCode(mobile);
}
private AjaxResult sendSmsCode(String mobile) {
String smsKey = "SMS_CODE:"+mobile;
// 一 先产生一个验证码
String smsCode = StrUtils.getComplexRandomString(4); //面条 饭
//二 如果原来验证码有效,替换生成哪个
String smsCodeForRedis= redisClient.get(smsKey); //smsCode格式 code:time
if (StringUtils.hasLength(smsCodeForRedis)){
//2如果有效,判断是否已经过了重发时间,如果没有过就报错
String[] split = smsCodeForRedis.split(":");
String timeStr = split[1];
long time = System.currentTimeMillis()-Long.valueOf(timeStr);
if (time<1000*60){
return AjaxResult.me().setSuccess(false).setMessage("请不要重复发送短信验证码!");
}
//如果过了,替换原来的验证码
smsCode = split[0];
}
//三 存储到redis
redisClient.addForTime(smsKey,smsCode+":"+System.currentTimeMillis(),60*5);//5分钟过期
//四 都要进行发送
System.out.println("[源码科技]已经发送验证码"+smsCode+"到用户手机:"+mobile);
Map<String,String> params = new HashMap<>();
//smsMob=手机号码&smsText=验证码:8888
params.put("smsMob",mobile);
params.put("smsText","验证码为:"+smsCode+",请在5分钟之内使用!");
smsClient.send(JSONObject.toJSONString(params));
return AjaxResult.me();
}
controller
public class SmsController {
@Autowired
private ISmsService smsService;
//1 以下两个放到常量类中
//Uid=本站用户名
//Key=接口安全秘钥
//2 以下两个是动态传入
// smsMob=手机号码&smsText=验证码:8888
@PostMapping
AjaxResult send(@RequestParam String params){
System.out.println(params);
Map<String,String> paramsTmp =
JSONObject.parseObject(params, Map.class);
//封装参数
paramsTmp.put("Uid", SmsContants.SMS_UID);
paramsTmp.put("Key", SmsContants.SMS_KEY);
return smsService.sendSmsCode(paramsTmp);
}
}
前台
sendSmsCode(event){
//1.判断手机号不为空
if(!this.formParams.mobile){
alert("手机号不能为空");
return;
}
//2.判断图片验证码不为空
if(!this.formParams.imageCode){
alert("图片验证码不能为空");
return;
}
//3.获取按钮,禁用按钮
var sendBtn = $(event.target);
sendBtn.attr("disabled",true);
var param = {
mobile: this.formParams.mobile,
imageCode:this.formParams.imageCode,
imageCodeKey:localStorage.getItem("imageCodeKey")
};
//4.发送ajax请求
this.$http.post("/user/verifycode/sendSmsCode",param).then(res=>{
var ajaxResult = res.data;
if(ajaxResult.success){
alert("手机验证码已经发送到您的手机,请在5分钟内使用");
//4.1.发送成:倒计时
var time = 60;
var interval = window.setInterval( function () {
//每一条倒计时减一
time = time - 1 ;
//把倒计时时间搞到按钮上
sendBtn.html(time+"秒后重发");
//4.2.倒计时完成恢复按钮
if(time <= 0){
sendBtn.html("重新发送");
sendBtn.attr("disabled",false);
//清除定时器
window.clearInterval(interval);
}
},1000);
}else{
//4.3.发送失败:提示,恢复按钮
sendBtn.attr("disabled",false);
alert("发送失败:"+ajaxResult.message);
}
})
}
注册实现
前台
submitRegister(){
//4.发送ajax请求
this.$http.post("/user/sso/register",this.formParams).then(res=>{
var ajaxResult = res.data;
if(ajaxResult.success){
alert("注册成功");
window.location.href = "http://127.0.0.1:6003/login.html";
}else{
alert("发送失败:"+ajaxResult.message);
}
})
}
controller
//注册
@PostMapping("/register")
/**
* mobile:'13330964748',
password:'123456',
imageCode:'',
smsCode:''
==============Map接口---不是通过feign,通过zuul转发过来外部请求
*/
public AjaxResult register(@RequestBody Map<String,String> params)
{
return ssoService.register(params);
}
service
@Override
public AjaxResult register(Map<String, String> params) {
String mobile = params.get("mobile");
String password = params.get("password");
String smsCode = params.get("smsCode");
//1 校验
AjaxResult ajaxResult = validateParam(mobile,password,smsCode);
if (!ajaxResult.isSuccess())
return ajaxResult;
// 2 保存sso信息
Sso sso = new Sso();
sso.setPhone(mobile);
//获取随机验证-32位随机字符串
String salt = StrUtils.getComplexRandomString(32);
sso.setSalt(salt);
//使用随机验证给密码md5加密,并设置
//输入密码+以后做校验的时候先从数据库查询盐=md5,再和数据库查询出来进行比较
String md5Password = MD5.getMD5(password + salt);
sso.setPassword(md5Password);
sso.setNickName(mobile);
sso.setSecLevel(0);
sso.setBitState(7L); // 二进制位来表示 2个小时(扩展)
sso.setCreateTime(System.currentTimeMillis());
ssoMapper.insert(sso);
// 3 保存关联对象信息vipbase
VipBase vipBase = new VipBase();
vipBase.setCreateTime(System.currentTimeMillis());
vipBase.setSsoId(sso.getId());
vipBase.setRegChannel(1);
vipBase.setRegTime(System.currentTimeMillis());
vipBaseMapper.insert(vipBase);
return AjaxResult.me();
}
private AjaxResult validateParam(String mobile,String password,String smsCode){
// 手机号密码null校验
if (!StringUtils.hasLength(mobile) || !StringUtils.hasLength(password))
return AjaxResult.me().setSuccess(false).setMessage("请输入用户名或密码!");
// 手机号时候已经注册
List<Sso> ssoList = ssoMapper.selectList(new EntityWrapper<Sso>().eq("phone", mobile));
if (ssoList!=null&&ssoList.size()>0)
return AjaxResult.me().setSuccess(false).setMessage("您已经注册过了!可以直接使用!");
// 短信验证码校验 通过key从redis获取
String smsCodeStr = redisClient.get("SMS_CODE:" + mobile); // code:time
String smsCodeOfRedis = smsCodeStr.split(":")[0];
if (!smsCodeOfRedis.equals(smsCode))
return AjaxResult.me().setSuccess(false).setMessage("请输入正确短信验证码");
return AjaxResult.me();
}
单点登陆
zuul拦截
package com.sx.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class LoginFilter extends ZuulFilter {
@Override
public String filterType() {
// 登录校验,肯定是在前置拦截
return "pre";
}
@Override
public int filterOrder() {
//顺序设置为1
return 1;
}
//登录放行
@Override
public boolean shouldFilter() {
// 返回true,代表过滤器生效。
return true;
}
@Override
public Object run() throws ZuulException {
// 登录校验逻辑。
// 1)获取Zuul提供的请求上下文对象
RequestContext ctx = RequestContext.getCurrentContext();
// 2) 从上下文中获取request对象
HttpServletRequest req = ctx.getRequest();
//对登录放行
String requestURI = req.getRequestURI();
System.out.println(requestURI+"jjjjjjjjjjjjjjjjjj");
if (requestURI.contains("login"))
return null;
if (requestURI.contains("api-docs"))
return null;
if(requestURI.contains("verifycode"))
return null;
// 3) 从请求中获取token
String token = req.getHeader("access-token");
// 4) 判断
if(token == null || "".equals(token.trim())){
// 没有token,登录校验失败,拦截
ctx.setSendZuulResponse(false);
// 返回401状态码。也可以考虑重定向到登录页。
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
// 校验通过,可以考虑把用户信息放入上下文,继续向后执行
return null;
}
}
后台
controller
//登录
@PostMapping("/login")
/**
* phone:'13330964748',
password:'123456',
*/
public AjaxResult login(@RequestBody Sso sso){
return ssoService.login(sso);
}
service
@Override
public AjaxResult login(Sso sso) {
//校验用户是否存在,状态ok,是否已经过期等待
if (!StringUtils.hasLength(sso.getPhone()) || !StringUtils.hasLength(sso.getPassword()))
return AjaxResult.me().setSuccess(false).setMessage("请输入用户名或密码!");
List<Sso> ssoList = ssoMapper.selectList(new EntityWrapper<Sso>()
.eq("phone", sso.getPhone()));
if (ssoList==null || ssoList.size()<1)
return AjaxResult.me().setSuccess(false).setMessage("用户不存在,请注册后再来登录!");
//从数据库查询sso
Sso ssoExsit = ssoList.get(0);
//进行密码比对-输入密码+数据库盐值=md5再和数据库密码比对
System.out.println(sso.getPassword());
System.out.println(ssoExsit.getSalt());
String md5Pwd = MD5.getMD5(sso.getPassword() + ssoExsit.getSalt());
System.out.println(md5Pwd);
if (!ssoExsit.getPassword().equals(md5Pwd)){
return AjaxResult.me().setSuccess(false).setMessage("请输入正确的用户名或密码!");
}
//用户存到redis并且返回token-60*30
String accessToken = UUID.randomUUID().toString();
redisClient.addForTime(accessToken, JSONObject.toJSONString(ssoExsit),30*60);
return AjaxResult.me().setResultObj(accessToken);
}
获取用户
controller
@GetMapping("/ac/{accessToken}")
public Sso querySso(@PathVariable("accessToken") String accessToken){
System.out.println(accessToken);
return ssoService.querySso(accessToken);
}
service
@Override
public Sso querySso(String accessToken) {
//登录时已经存放了redis,直接从redis获取就ok
String sso = redisClient.get(accessToken);
return JSONObject.parseObject(sso,Sso.class);
}
前台实现
login(){
//4.发送ajax请求
this.$http.post("/user/sso/login",this.formParams).then(res=>{
var ajaxResult = res.data;
if(ajaxResult.success){
alert("登录成功");
let accessToken = ajaxResult.resultObj;
//通过cookie共享accessToken给其他站点 user不能直接放入cookie,因为不安全,但是可以
//存放access-token到时通过access-token就能获取用户了
setCookie("access-token",accessToken,"m30"); //session过期也是30分钟
//保存用户到localStorage
this.$http.get("/user/sso/ac/"+accessToken).then(res=>{
var user = res.data;
localStorage.setItem("user",user)
})
//跳转到主页面 localhost和127.0.0.1不同的不能共享cookie
//location.href = "http://user.hrm.com:6003/user.home.html"
location.href = "http://127.0.0.1:6003/user.home.html"
//以后所有对后端服务的访问都要携带accessToken
}else{
alert("登录失败:"+ajaxResult.message);
}
})
common.js
/JS操作cookies方法!
//读取cookies
function getCookie(name)
{
var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
if(arr=document.cookie.match(reg)) return unescape(arr[2]);
else return null;
}
//删除cookies
function delCookie(name)
{
var exp = new Date();
exp.setTime(exp.getTime() - 1);
var cval=getCookie(name);
if(cval!=null) document.cookie= name + "="+cval+";expires="+exp.toGMTString();
}
//使用示例
// setCookie("name","hayden");
// alert(getCookie("name"));
//如果需要设定自定义过期时间
//那么把上面的setCookie 函数换成下面两个函数就ok;
//写cookies
function setCookie(name,value)
{
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + Days*24*60*60*1000);
document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();
}
//程序代码
function setCookie(name,value,time){
var strsec = getsec(time);
var exp = new Date();
exp.setTime(exp.getTime() + strsec*1);
//所以hrm.com为父域名的任何路径都能共享cookie
//document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString()+";path=/"+";domain=.hrm.com";
document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString()+";path=/";
}
function getsec(str){
var str1=str.substring(1,str.length)*1;
var str2=str.substring(0,1);
if (str2=="s"){
return str1*1000;
}else if (str2=="h"){
return str1*60*60*1000;
}else if (str2=="m"){
return str1*60*1000;
}else if (str2=="d"){
return str1*24*60*60*1000;
}
}
//这是有设定过期时间的使用示例:
//s20是代表20秒
//h是指小时,如12小时则是:h12
//d是天数,30天则:d30
//暂时只写了这三种,不知道谁有更好的方法,呵呵
// setCookie("name","hayden","s20");
//axios初始化
axios.interceptors.request.use(config => {
//如果已经登录了,每次都把token作为一个请求头传递过程
let accessToken = getCookie("access-token");
if (accessToken) {
// 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
config.headers['access-token'] = accessToken;
}
console.debug('config',config)
return config
}, error => {
// Do something with request error
Promise.reject(error)
})
axios.defaults.baseURL = "http://127.0.0.1:1030/services"//配置前缀
Vue.prototype.$http = axios //给Vue这个类添加一个原型的属性,这个类的对象都能调用
Vue.config.productionTip = false
//登录拦截判断 时候有accessToken
//是否能从localStrage获取获取用户,如果有自己跳过
//否则需要获取用户,再跳过
//var loginUrl = "http://user.hrm.com:6003/login.html"
var loginUrl = "http://127.0.0.1:6003/login.html"
$().ready(function(){
// 登录拦截 要放行 login.html register
let href = location.href;
if(href.indexOf("login")!=-1 || href.indexOf("reg") !=-1)
return;
let accessToken = getCookie("access-token");
if(!accessToken)
location.href = loginUrl;
let user = localStorage.getItem("user");
if(!user){
//保存用户到localStorage
axios.get("/user/sso/ac/"+accessToken).then(res=>{
var user = res.data;
localStorage.setItem("user",user)
})
}
})
其他站点: 登录拦截,获取ac,进而获取用户存放localstorage
common.js
//JS操作cookies方法!
//读取cookies
function getCookie(name)
{
var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
if(arr=document.cookie.match(reg)) return unescape(arr[2]);
else return null;
}
//删除cookies
function delCookie(name)
{
var exp = new Date();
exp.setTime(exp.getTime() - 1);
var cval=getCookie(name);
if(cval!=null) document.cookie= name + "="+cval+";expires="+exp.toGMTString();
}
//使用示例
// setCookie("name","hayden");
// alert(getCookie("name"));
//如果需要设定自定义过期时间
//那么把上面的setCookie 函数换成下面两个函数就ok;
//写cookies
function setCookie(name,value)
{
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + Days*24*60*60*1000);
document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();
}
//程序代码
function setCookie(name,value,time){
var strsec = getsec(time);
var exp = new Date();
exp.setTime(exp.getTime() + strsec*1);
document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString()+";path=/";
}
function getsec(str){
var str1=str.substring(1,str.length)*1;
var str2=str.substring(0,1);
if (str2=="s"){
return str1*1000;
}else if (str2=="h"){
return str1*60*60*1000;
}else if (str2=="m"){
return str1*60*1000;
}else if (str2=="d"){
return str1*24*60*60*1000;
}
}
//这是有设定过期时间的使用示例:
//s20是代表20秒
//h是指小时,如12小时则是:h12
//d是天数,30天则:d30
//暂时只写了这三种,不知道谁有更好的方法,呵呵
// setCookie("name","hayden","s20");
//axios初始化
axios.interceptors.request.use(config => {
//如果已经登录了,每次都把token作为一个请求头传递过程
let accessToken = getCookie("access-token");
if (accessToken) {
// 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
config.headers['access-token'] = accessToken;
}
console.debug('config',config)
return config
}, error => {
// Do something with request error
Promise.reject(error)
})
axios.defaults.baseURL = "http://127.0.0.1:1030/services"//配置前缀
Vue.prototype.$http = axios //给Vue这个类添加一个原型的属性,这个类的对象都能调用
Vue.config.productionTip = false
//登录拦截判断 时候有accessToken
//是否能从localStrage获取获取用户,如果有自己跳过
//否则需要获取用户,再跳过
//var loginUrl = "http://user.hrm.com:6003/login.html"
var loginUrl = "http://127.0.0.1:6003/login.html"
$().ready(function(){
// 登录拦截 要放行 login.html register
let href = location.href;
if(href.indexOf("login")!=-1 || href.indexOf("reg") !=-1)
return;
let accessToken = getCookie("access-token");
if(!accessToken)
location.href = loginUrl;
let user = localStorage.getItem("user");
if(!user){
//保存用户到localStorage
axios.get("/user/sso/ac/"+accessToken).then(res=>{
var user = res.data;
localStorage.setItem("user",user)
})
}
})
注意:
1 一定要保证同域名
2 引入js顺序 common.js是需要依赖axios
Docker
先在VMware 中安装 CentOS(桥接模式)
使用ip addr命令查看本地IP
然后在cmd中ping ip地址 测试网络是否联通
然后在root登陆的终端中输入
yum install docker
查看Docker版本
docker version
启动与停止Docker
ystemctl命令是系统服务管理器指令,它是 service 和 chkconfig 两个命令组合。
启动docker:systemctl start docker
停止docker:systemctl stop docker
重启docker:systemctl restart docker
查看docker状态:systemctl status docker
开机启动:systemctl enable docker
查看docker概要信息:docker info
查看docker帮助文档:docker --help
列出镜像
docker images
REPOSITORY:镜像所在的仓库名称
TAG:镜像标签
IMAGE ID:镜像ID
CREATED:镜像的创建日期(不是获取该镜像的日期)
SIZE:镜像大小
这些镜像都是存储在Docker宿主机的/var/lib/docker目录下
搜索镜像
docker search 镜像名称
阿里镜像
编辑该文件:vi /etc/docker/daemon.json // 如果该文件不存在就手动创建;说明:在centos7.x下,通过vi。
)在该文件中输入如下内容:(就截图下面的那一团代码)
删除镜像
1、docker rmi $IMAGE_ID:删除指定镜像
2、docker rmi docker images -q
:删除所有镜像
Docker 容器操作
查看正在运行容器
docker ps
交互式容器 (只有测试的时候用)
创建一个交互式容器并取名为mycentos
docker run -it --name=mycentos centos:7 /bin/bash
这时我们通过ps命令查看,发现可以看到启动的容器,状态为启动状态
使用exit命令 退出当前容器
然后用ps -a 命令查看发现该容器也随之停止:
退出容器马上停止.
启动容器执行容器中的shell:
… /bin/bash -c ‘java -jar /usr/local/java/demo-0.0.1-SNAPSHOT.jar’
守护式容器
创建一个守护式容器:如果对于一个需要长期运行的容器来说,我们可以创建一个守护式容器。命令如下(容器名称不能重复):
docker run -di --name=mycentos2 centos:7
登录守护式容器方式:
docker exec -it container_name (或者 container_id) /bin/bash(exit退出时,容器不会停止)
进去以后出来不会关闭
停止与启动容器
停止正在运行的容器:docker stop 容器名
启动已运行过的容器:docker start 容器名