SAAS-HRM-day11(用户中心)

  • 1. 搭建用户模块中心环境
    • 1.1 后端环境搭建
      • 1.1.1 步骤分析
      • 1.1.2 步骤实现
    • 1.2 前端环境搭建
  • 2. 相关技术的实现
    • 2.1 随机数
    • 2.2 redis存带过期时间的数据
    • 2.3 图形验证码
      • 2.3.1 非前后端分离
      • 2.3.2 前后端分离
        • 2.3.2.1 方案说明
        • 2.3.2.1 方案实现
    • 2.4 短信验证码

1. 搭建用户模块中心环境

1.1 后端环境搭建

1.1.1 步骤分析

  1. 创建模块
  2. 导包
  3. 配置
  4. 入口类
  5. 日志
  6. 网关
  7. swagger
  8. 生成代码
  9. 分页插件配置
  10. 测试

1.1.2 步骤实现

  1. 创建模块
  • hrm_parent
    • hrm_user_parent
      • hrm_user_common
      • hrm_user_client
      • hrm_user_service
  1. 导包
  • common
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
        
            cn.wangningbo.hrm
            hrm_basic_util
            1.0-SNAPSHOT
        
        
        
            com.baomidou
            mybatis-plus
            2.2.0
        
  • client
        
        
            cn.wangningbo.hrm
            hrm_user_common
            1.0-SNAPSHOT
        
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        
  • service
        
        
            cn.wangningbo.hrm
            hrm_user_common
            1.0-SNAPSHOT
        
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        
        
        
            io.springfox
            springfox-swagger2
            2.9.2
        
        
        
            io.springfox
            springfox-swagger-ui
            2.9.2
        
        
        
            org.springframework.cloud
            spring-cloud-starter-config
        
        
        
            com.baomidou
            mybatis-plus-boot-starter
            2.2.0
        
        
        
            mysql
            mysql-connector-java
        
        
        
            cn.wangningbo.hrm
            hrm_basic_redis_client
            1.0-SNAPSHOT
        
        
        
            com.cloopen
            CCP_REST_SMS_SDK_JAVA
            v2.6.3r
        
  1. 配置
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
  1. 入口类
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);
    }
}
  1. 日志



    
    
    
    
    
    
        
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
        
    

      
    
        
        ${LOG_HOME}/${appName}/${appName}.log
        
        
            
            ${LOG_HOME}/${appName}/${appName}-%d{yyyy-MM-dd}-%i.log
            
            365
            
            
                100MB
            
        
             
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n
        
    

    
    
    
    
    



    
    
        
        
    
 
  1. 网关

在网关的配置文件里面配置

zuul:
    routes:
        pageAgent.serviceId: hrm-page-agent # 服务名
        pageAgent.path: /pageAgent/** # 把pageAgent打头的所有请求都转发给hrm-page-agent
  1. 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", "", "[email protected]"))
                .version("1.0")
                .build();
    }

}

网关配置

resources.add(swaggerResource("用户中心系统", "/services/user/v2/api-docs", "2.0"));
  1. 生成代码

配置文件

#代码放到哪儿
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 map = new HashMap();
                map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-rb");
                this.setMap(map);
            }
        };

        List focList = new ArrayList();


        // 调整 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();
    }

}
  1. 分页插件配置
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();
    }
}
  1. 测试

    postman测试

1.2 前端环境搭建

  1. 创建模块
  2. 拷贝html
  3. 运行
live-server --port=6003
  1. 后端配置跨域放行

在网关那里配置一下

        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 非前后端分离

  1. 后台生成验证码放入session
  2. 后台把验证码写入图片,把图片以流的方式返回给用户
  3. 用户根据图片输入验证码,后台获取到后从seession获取出来做比对.

2.3.2 前后端分离

前后端分离-session不能使用

前端的图片标签的src属性,不仅仅可以存放地址,也可以存放base64来显示图片

2.3.2.1 方案说明

html5有两种在浏览器的存放数据机制

  1. localStorage:持久化,不删除一直存在
  2. sessionStorage:前端session

验证码流程:

  1. 用户使用uuid作为验证码的key,并把uuid生成的key存放在localStorage
  2. 前端发送请求,通过key获取验证码
  3. 后端生成6位验证码,存放到redis,key为前端传过来的key,value为生成的6位验证码进行base64加密后的结果,过期时间设置为5分钟。把验证码写入图片,把图片使用base64编码返回
  4. 前端展示后端返回的base64图片验证码
  5. 用户输入验证码,发送请求,把key和用户输入的验证码传递到后端
  6. 后端把用户传入的验证码通过base64加密,后端根据前端传过来的key从redis中获取验证码,比对是否与用户传过来的验证码相同。

2.3.2.1 方案实现

  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();
        }
  1. 后端的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);
    }
}
  1. IImageValidateCodeService
package cn.wangningbo.hrm.service;

public interface IImageValidateCodeService{
    String getCode(String uuid);
}
  1. 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());
    }
}
  1. 刷新前端,即可看到验证码已出现!

2.4 短信验证码

  1. 导包(已经导入了,自己maven安装的那个)
  2. 发送短信的工具
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 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 data = (HashMap) result.get("data");
            Set 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"));
        }
    }
}
  1. 测试
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"});
    }
}

你可能感兴趣的:(SAAS-HRM-day11(用户中心))