当前位置: 首页 > wzjs >正文

比较好的做网站的公司景区门户网站建设的必要性

比较好的做网站的公司,景区门户网站建设的必要性,一般通过什么饮食摄入钙,网站建设和谷歌优化我们接着进行抽奖系统的完善。 前面我们完成了 1.结构初始化(统一结果返回之类的,还有包的分类) 2.加密(基于Hutool进行的对称与非对称加密) 3.用户注册 接下来我们先完善一下结构(统一异常处理&#…

我们接着进行抽奖系统的完善。

前面我们完成了

1.结构初始化(统一结果返回之类的,还有包的分类)

2.加密(基于Hutool进行的对称与非对称加密)

3.用户注册

接下来我们先完善一下结构(统一异常处理)

1.统一异常处理

很简单,@RestControllerAdvice+@ExceptionHandler即可

@RestControllerAdvice//可以捕获全局抛出的异常
@ResponseBody
public class GlobalExceptionHandler {private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);//捕获Service层的异常@ExceptionHandler(value = ServiceException.class)public CommonResult<?> serviceException(ServiceException e) {logger.error("serviceException :", e);//返回数据return CommonResult.error(GlobalErrorCodeConstant.INTERNAL_SERVICE_ERROR);}//捕获Controller层的异常@ExceptionHandler(value = ControllerException.class)public CommonResult<?> controllerException(ControllerException e) {logger.error("controllerException :", e);//返回数据return CommonResult.error(GlobalErrorCodeConstant.INTERNAL_SERVICE_ERROR);}//捕获全局的异常@ExceptionHandler(value = Exception.class)public CommonResult<?> exception(Exception e) {logger.error("exception :", e);//返回数据return CommonResult.error(GlobalErrorCodeConstant.INTERNAL_SERVICE_ERROR);}
}

这里使用@ExceptionHandler(value = 类名)的方式捕获异常。

其他没什么要特别注意的,补充GlobalErrorCodeConstant的异常种类

2.登录模块

这里提供短信验证码登录的方式,因此我们要了解一下阿里短信服务

但是现在个人申请不到了,所以我们直接使用虚拟的验证码吧。

附一个申请成功后植入的代码:

1.阿里短信代码模块:

依赖:

<dependency><groupId>com.aliyun</groupId><artifactId>dysmsapi20170525</artifactId><version>2.0.24</version>
</dependency>

短信服务工具类:

@Component
public class SMSUtil {private static final Logger logger = LoggerFactory.getLogger(SMSUtil.class);@Value(value = "${sms.sign-name}")private String signName;@Value(value = "${sms.access-key-id}")private String accessKeyId;@Value(value = "${sms.access-key-secret}")private String accessKeySecret;/*** 发送短信** @param templateCode 模板号* @param phoneNumbers 手机号* @param templateParam 模板参数 {"key":"value"}*/public void sendMessage(String templateCode, String phoneNumbers, String templateParam) {try {Client client = createClient();com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest = new SendSmsRequest().setSignName(signName).setTemplateCode(templateCode).setPhoneNumbers(phoneNumbers).setTemplateParam(templateParam);RuntimeOptions runtime = new RuntimeOptions();SendSmsResponse response = client.sendSmsWithOptions(sendSmsRequest, runtime);if (null != response.getBody()&& null != response.getBody().getMessage()&& "OK".equals(response.getBody().getMessage())) {logger.info("向{}发送信息成功,templateCode={}", phoneNumbers, templateCode);return;}logger.error("向{}发送信息失败,templateCode={},失败原因:{}",phoneNumbers, templateCode, response.getBody().getMessage());} catch (TeaException error) {logger.error("向{}发送信息失败,templateCode={}", phoneNumbers, templateCode, error);} catch (Exception _error) {TeaException error = new TeaException(_error.getMessage(), _error);logger.error("向{}发送信息失败,templateCode={}", phoneNumbers, templateCode, error);}}/*** 使用AK&SK初始化账号Client* @return Client*/private Client createClient() throws Exception {// 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。// 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378657.html。Config config = new Config().setAccessKeyId(accessKeyId).setAccessKeySecret(accessKeySecret);// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapiconfig.endpoint = "dysmsapi.aliyuncs.com";return new Client(config);}}

配置:

### 短信 ##
## 短信 ##
sms.access-key-id="填写⾃⼰申请的"
sms.access-key-secret="填写⾃⼰申请的"
sms.sign-name="填写⾃⼰申请的"

代码使用:

使用时传入3个参数:

templateCode(模板号):SMS_465324787

phoneNumbers(手机号):传入你申请的

templateParam(模版参数):发送验证码的格式设置为{"key":"value"}

使用时传入一个map并将其序列化

2.验证码模块

利用Hutool工具:(这工具真好用都不用手写了)

//生成随机验证码
public class CaptchaUtil {/*** 生成随机验证码** @param length  几位* @return*/public static String getCaptcha(int length) {// 自定义纯数字的验证码(随机4位数字,可重复)RandomGenerator randomGenerator = new RandomGenerator("0123456789", length);LineCaptcha lineCaptcha = cn.hutool.captcha.CaptchaUtil.createLineCaptcha(200, 100);lineCaptcha.setGenerator(randomGenerator);// 重新生成codelineCaptcha.createCode();return lineCaptcha.getCode();}}

3.Controller

基于手机号生成验证码并发送验证码   最后使用Redis缓存验证码用于校验

    /*** 发送验证码* @param phoneNumber* @return*/@RequestMapping("/verification-code/send")public CommonResult<Boolean> verificationCode (String phoneNumber) {//日志打印logger.info("verificationCode  phoneNumber:{}", phoneNumber);verificationCodeService.sendVerificationCode(phoneNumber);return CommonResult.success(Boolean.TRUE);}

4.Service

 

    /*** 发送验证码* @param phoneNumber* @return*/public String sendVerificationCode(String phoneNumber);/*** 获取验证码* @param phoneNumber* @return*/public String getVerificationCode(String phoneNumber);
    @Overridepublic String sendVerificationCode(String phoneNumber) {//校验手机号if(!RegexUtil.checkMobile(phoneNumber)) {throw new ServiceException(ServiceErrorCodeConstant.PHONE_NUMBER_ERROR);}//生成随机验证码  利用hutool生成String code = CaptchaUtil.getCaptcha(4);//发送验证码Map<String, String> map = new HashMap<>();map.put("code", code);smsUtil.sendMessage(PHONE_NUMBER_TEMPLATE_CODE, phoneNumber, JacksonUtil.writeValueAsString(map));//缓存验证码redisUtil.set(PHONE_NUMBER_PRE +phoneNumber, code, PHONE_NUMBER_TIMEOUT);return redisUtil.get(PHONE_NUMBER_PRE +phoneNumber);}@Overridepublic String getVerificationCode(String phoneNumber) {//校验手机号if(!RegexUtil.checkMobile(phoneNumber)) {throw new ServiceException(ServiceErrorCodeConstant.PHONE_NUMBER_ERROR);}return redisUtil.get(PHONE_NUMBER_PRE +phoneNumber);}

Redis使用简单介绍:

配置:

在Linux服务器上通过隧道开放Redis的6379端口号

在Linux上输入命令启动Redis:

service redis-server start

 可以在idea上下载插件Redis Helper

刷新后在右侧找到插件点击加号添加Redis服务 

 

 

1.名称(随便,便于标识)

2.本机就行

3.在Linux隧道绑定的端口号

4.Test测试验证,出现绿色即成功连接Redis 

 Redis使用测试:

    @Testvoid redisTest() {stringRedisTemplate.opsForValue().set("key1", "value2");System.out.println("从redis中获取value : " + stringRedisTemplate.opsForValue().get("key1"));}@Testvoid redisUtil() {
//        redisUtil.set("key2", "value2");
//        redisUtil.set("key3", "value3", 20L);
//        System.out.println("key2是否存在: " + redisUtil.hasKey("key2"));
//        System.out.println("key3是否存在: " + redisUtil.hasKey("key3"));//        redisUtil.delete("key2");
//        System.out.println("key2是否存在: " + redisUtil.hasKey("key2"));System.out.println("key3是否存在: " + redisUtil.hasKey("key3"));}

1.使用前注入StringRedisTemplate类

2.使用stringRedisTemplate.opsForValue().set("key1", "value2");添加元素

但是,每次使用都要注入StringRedisTemplate有点麻烦了,我们将其封装成一个util工具。

@Configuration
public class RedisUtil {public static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);/*** StringRedisTemplate : 直接用String存储读取(可读)* RedisTemplate : 先将被存储数据转换为字节数组(不可读)  再存储到Redis中 读取时以字节数组读取*/@Autowiredprivate StringRedisTemplate stringRedisTemplate;/*** 设置值* @param key* @param value* @return*/public boolean set(String key,String value) {try{stringRedisTemplate.opsForValue().set(key, value);return true;}catch(Exception e) {logger.error("RedisUtil set 错误:({}, {})", key, value, e);return false;}}/*** 设置带有过期时间的值* @param key* @param value* @param time  单位:秒* @return*/public boolean set(String key,String value, Long time) {try{stringRedisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);return true;}catch(Exception e) {logger.error("RedisUtil set 错误:({}, {}, {})", key, value, time, e);return false;}}/*** 获取值* @param key* @return*/public String get(String key) {try{return StringUtils.hasText(key)? stringRedisTemplate.opsForValue().get(key): null;}catch(Exception e) {logger.error("RedisUtil set 错误:({})", key, e);return null;}}/*** 删除值* @param key* @return*/public boolean delete(String... key) {try{if(key != null && key.length > 0) {if(key.length == 1) {stringRedisTemplate.delete(key[0]);}else {stringRedisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));}}return true;}catch(Exception e) {logger.error("RedisUtil delete 错误:({})", key, e);return false;}}/*** 查看Key是否存在* @param key* @return*/public boolean hasKey(String key) {return StringUtils.hasText(key)? stringRedisTemplate.hasKey(key): false;}}

该类提供了:

set:两个,一个是传入key:value  。另一个是传入key:value+过期时间

get:通过key获取value

delete:通过key,进行批量删除

hasKey:查看key是否存在

5.JWT令牌验证

前面我们发送了验证码,在客户端我们就要将该验证码输入进行登录,但还有一些用户会采用手机号/邮箱+密码的方式登录。所以我们要开放两个接口用于登录。

登录完后端会返回给前端一个token用于令牌校验,使验证码登录在多台主机下都可以使用。

JWT本身没什么说的,直接上代码:

public class JWTUtil {private static final Logger logger = LoggerFactory.getLogger(JWTUtil.class);/*** 密钥:Base64编码的密钥*/private static final String SECRET = "SDKltwTl3SiWX62dQiSHblEB6O03FG9/vEaivFu6c6g=";/*** 生成安全密钥:将一个Base64编码的密钥解码并创建一个HMAC SHA密钥。*/private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(Decoders.BASE64.decode(SECRET));/*** 过期时间(单位: 毫秒)*/private static final long EXPIRATION = 60*60*1000;/*** 生成密钥** @param claim  {"id": 12, "name":"张山"}* @return*/public static String genJwt(Map<String, Object> claim){//签名算法String jwt = Jwts.builder().setClaims(claim)             // 自定义内容(载荷).setIssuedAt(new Date())      // 设置签发时间.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) // 设置过期时间.signWith(SECRET_KEY)         // 签名算法.compact();return jwt;}/*** 验证密钥*/public static Claims parseJWT(String jwt){if (!StringUtils.hasLength(jwt)){return null;}// 创建解析器, 设置签名密钥JwtParserBuilder jwtParserBuilder = Jwts.parserBuilder().setSigningKey(SECRET_KEY);Claims claims = null;try {//解析tokenclaims = jwtParserBuilder.build().parseClaimsJws(jwt).getBody();}catch (Exception e){// 签名验证失败logger.error("解析令牌错误,jwt:{}", jwt, e);}return claims;}/*** 从token中获取用户ID*/public static Integer getUserIdFromToken(String jwtToken) {Claims claims = JWTUtil.parseJWT(jwtToken);if (claims != null) {Map<String, Object> userInfo = new HashMap<>(claims);return (Integer) userInfo.get("userId");}return null;}
}

提供了三个方法:

1.getJWT:通过传入一个map生成token并返回。

2.parseJWT:将token解析为map数据

3.getUserIdFromToken:从token中获取用户ID(可能有用)

6.管理员登录(两种⽅式)

学习令牌的使⽤之后, 接下来我们通过令牌来完成⽤⼾的登录的流程

1. 登陆⻚⾯把⽤⼾名密码提交给服务器.

2. 服务器端验证⽤⼾名密码是否正确, 如果正确, 服务器⽣成令牌, 下发给客⼾端.

3. 客⼾端把令牌存储起来(⽐如Cookie, local storage等), 后续请求时, 把token发给服务器

4. 服务器对令牌进⾏校验, 如果令牌正确, 进⾏下⼀步操作

7.Controller

数据准备:

接收与返回的数据:

因为两个登录都含有统一数据,所以对公共的进行提取

//共有的字段
@Data
public class UserLoginParam implements Serializable {/*** 身份登录信息* 可填可不填  不填代表都可以登录*/private String mandatoryIdentity;
}

UserPasswordLoginParam:

@Data
@EqualsAndHashCode(callSuper = true)
public class UserPasswordLoginParam extends UserLoginParam{/*** 手机号或邮箱*/@NotBlank(message = "手机号或邮箱不能为空")private String loginName;/*** 密码*/@NotBlank(message = "密码不能为空")private String password;
}

UserMessageLoginParam:

@Data
@EqualsAndHashCode(callSuper = true)
public class UserMessageLoginParam extends UserLoginParam{/*** 登录手机号*/@NotBlank(message = "手机号不能为空")private String loginMobile;/*** 验证码*/@NotBlank(message = "验证码不能为空")private String code;
}

 

UserLoginResult:

@Data
public class UserLoginResult implements Serializable {/*** 令牌*/@NotBlank(message = "令牌不能为空")private String token;/*** 身份信息*/@NotBlank(message = "身份信息不能为空")private String identity;
}

1.密码登录:

    /*** 密码登录* @param userLoginParam* @return*/@RequestMapping("/password/login")private CommonResult<UserLoginResult> passwordLogin(@Validated @RequestBody UserPasswordLoginParam userLoginParam) {logger.info("passwordLogin userLoginParam:{}", JacksonUtil.writeValueAsString(userLoginParam));//使用同一个接口完成登录UserLoginDTO userLoginDTO = userService.login(userLoginParam);return CommonResult.success(convertToLoginResult(userLoginDTO));}

在数据库返回的对象使用DTO,然后返回时用convertToLoginResult进行类型转化。

    private UserLoginResult convertToLoginResult(UserLoginDTO userLoginDTO) {//校验if(userLoginDTO == null) {throw new ControllerException(ControllerErrorCodeConstant.LOGIN_ERROR);}//数据转化UserLoginResult userLoginResult = new UserLoginResult();userLoginResult.setToken(userLoginDTO.getToken());userLoginResult.setIdentity(userLoginDTO.getIdentity().name());return userLoginResult;}

2.短信验证码登录

与之类似:

    /*** 短信验证码登录* @param userLoginParam* @return*/@RequestMapping("/message/login")private CommonResult<UserLoginResult> messageLogin(@Validated @RequestBody UserMessageLoginParam userLoginParam) {logger.info("messageLogin userLoginParam:{}", JacksonUtil.writeValueAsString(userLoginParam));//使用同一个接口完成登录  将接收参数改为公用参数extendsUserLoginDTO userLoginDTO = userService.login(userLoginParam);return CommonResult.success(convertToLoginResult(userLoginDTO));}

8.Sevice

通过Java14特性属性校验并赋值实现一个接口完成两个登录功能

    /*** 用户登录*   1.手机号/邮箱 + 密码*   2.手机号 + 验证码* @param userLoginParam* @return*/@Overridepublic UserLoginDTO login(UserLoginParam userLoginParam) {UserLoginDTO userLoginDTO = null;//类型检查与类型交换  java 14 版本及以上  实现校验两个登录方式if(userLoginParam instanceof UserPasswordLoginParam loginParam) {//手机号/邮箱 + 密码userLoginDTO = loginByPassword(loginParam);}else if(userLoginParam instanceof UserMessageLoginParam loginParam) {//手机号 + 验证码userLoginDTO = loginByShortMessage(loginParam);}else {throw new ServiceException(ServiceErrorCodeConstant.LOGIN_INFO_NOT_EXITS);}return userLoginDTO;}

DTO:

@Data
public class UserLoginDTO implements Serializable {/*** JWT令牌*/private String token;/*** 身份信息*/private UserIdentityEnum identity;
}

1.通过手机短信登录

1.校验手机号

        if(!StringUtils.hasText(loginParam.getLoginMobile())) {throw new ServiceException(ServiceErrorCodeConstant.PHONE_NUMBER_ERROR);}

2.通过手机号完成数据库查询

        UserDO userDO = userMapper.selectByPhoneNumber(new Encrypt(loginParam.getLoginMobile()));

3.校验数据库信息及身份(判断是否是管理员)

        //校验数据库数据if(userDO == null) {throw new ServiceException(ServiceErrorCodeConstant.USER_INFO_IS_EMPTY);}else if(StringUtils.hasText(loginParam.getMandatoryIdentity())&& !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {//身份校验不通过throw new ServiceException(ServiceErrorCodeConstant.IDENTITY_ERROR);}

4.获取Redis中的验证码并于数据库中的数据进行校验 

        String code = verificationCodeService.getVerificationCode(loginParam.getLoginMobile());if(!code.equals(loginParam.getCode())) {throw new ServiceException(ServiceErrorCodeConstant.VERIFICATION_CODE_ERROR);}

5.将数据封装成一个token

        //塞入返回值(JWT)Map<String, Object> claim = new HashMap<>();claim.put("id", userDO.getId());claim.put("identity", userDO.getIdentity());String token = JWTUtil.getJwt(claim);

6.封装成为DTO返回

        UserLoginDTO userLoginDTO = new UserLoginDTO();userLoginDTO.setToken(token);userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));return userLoginDTO;

2.通过手机号/邮箱+密码登录

1.校验密码

2.判断是手机号还是邮箱

3.通过手机号/邮箱获取数据库信息

4.校验 数据库信息及身份信息

5.生成token

6.包装成DTO返回

    /*** 通过手机号+密码登录* @param loginParam* @return*/private UserLoginDTO loginByPassword(UserPasswordLoginParam loginParam) {UserDO userDO = null;if(!StringUtils.hasText(loginParam.getPassword())) {throw new ServiceException(ServiceErrorCodeConstant.PASSWORD_EMPTY);}//判断是手机号还是邮箱登录if(RegexUtil.checkMobile(loginParam.getLoginName())) {//手机号//根据手机号查询用户表userDO = userMapper.selectByPhoneNumber(new Encrypt(loginParam.getLoginName()));}else if(RegexUtil.checkMail(loginParam.getLoginName())) {//邮箱登录//根据邮箱查询用户表userDO = userMapper.selectByEmail(loginParam.getLoginName());}else {throw new ServiceException(ServiceErrorCodeConstant.LOGIN_NOT_EXITS);}//校验登录信息if(userDO == null) {throw new ServiceException(ServiceErrorCodeConstant.USER_INFO_IS_EMPTY);}else if(StringUtils.hasText(loginParam.getMandatoryIdentity())&& !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {//身份校验不通过throw new ServiceException(ServiceErrorCodeConstant.IDENTITY_ERROR);}else if(!DigestUtil.sha256Hex(loginParam.getPassword()).equals(userDO.getPassword())) {throw new ServiceException(ServiceErrorCodeConstant.PASSWORD_ERROR);}//塞入返回值(JWT)Map<String, Object> claim = new HashMap<>();claim.put("id", userDO.getId());claim.put("identity", userDO.getIdentity());String token = JWTUtil.getJwt(claim);UserLoginDTO userLoginDTO = new UserLoginDTO();userLoginDTO.setToken(token);userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));return userLoginDTO;}


文章转载自:

http://v4a7CCEy.fnnkL.cn
http://dL4QRYa0.fnnkL.cn
http://8y9ht3bL.fnnkL.cn
http://3rGD24TX.fnnkL.cn
http://ShAwgW6k.fnnkL.cn
http://vywDhvbD.fnnkL.cn
http://hVg9CrFg.fnnkL.cn
http://RpCwm9Hg.fnnkL.cn
http://JpICESkD.fnnkL.cn
http://mJ14lx9i.fnnkL.cn
http://fOA7d7y8.fnnkL.cn
http://gYSK5FdW.fnnkL.cn
http://CVgNRcYo.fnnkL.cn
http://6FjC2CBa.fnnkL.cn
http://VeTUj0fD.fnnkL.cn
http://soIT5lqA.fnnkL.cn
http://SPSOShjF.fnnkL.cn
http://BOLelQpt.fnnkL.cn
http://JOuAiXuK.fnnkL.cn
http://5gl6TA8C.fnnkL.cn
http://niE9OJXo.fnnkL.cn
http://hpi1Q6KJ.fnnkL.cn
http://WBv9YxGv.fnnkL.cn
http://WFc1hzbq.fnnkL.cn
http://YZMfeL7t.fnnkL.cn
http://MHm2Tvbh.fnnkL.cn
http://Qu4KN9yL.fnnkL.cn
http://W3TluyZn.fnnkL.cn
http://u5Bt7XmN.fnnkL.cn
http://1EGKQaBc.fnnkL.cn
http://www.dtcms.com/wzjs/690607.html

相关文章:

  • vuejs 可做网站吗小红书广告代理商
  • 哪个网站能免费做电子书封面广州网站开发服务
  • 网站设计中下拉列表怎么做杭州集团公司网站建设
  • 网站建设学什么WordPress邮件代发
  • 站长平台社区云服务器是虚拟技术吗
  • 网站后台 生成所有页面网站源码44444kt
  • 响应式网站的开发做博客网站
  • 如何用电脑做网站服务器吗广州网站优化运营
  • asp旅游网站模板下载新闻发布会的意义
  • 陕西省信用建设门户网站去了哪找网站建设公司
  • 阿雷网站建设公司无锡网站建设优化公司
  • 网站开发的职位要求自己做网站怎样挣钱
  • 我需要网站上海商标注册
  • 上海陆海建设集团网站男装网站模板演示
  • 个人网站要求天津市建设执业资格注册中心网站
  • 门户网站系统程序如何用wd做网站设计
  • 大家都在哪些网站做宣传免费的短视频素材库
  • 化工厂网站建设建设部网站怎么查询企业业绩
  • 网站在建设中是什么意思百度网站建设公司哪家好
  • 网站宽屏版ps做登录网站
  • 长沙做网站优化漳州做网站建设公司
  • jquery插件网站推荐糖果果屋网站建设规划书
  • wordpress 模板汉化网站首页优化方案
  • 网站建设是怎么挣钱天津网站快速备案
  • 安徽集团网站建设WORDPRESS导购主题:WYZDG
  • 济南公司网站建设公司排名怎么在网站上做404页面
  • 河北手机版建站系统开发网站怎么做留言区
  • 用jsp做的网站有哪些大企业网站建设费用
  • 北京网站建设工作室哪家好网站怎样推广 优帮云
  • 刷百度指数网站网页qq邮箱怎么发文件给别的邮箱