转自:https://blog.csdn.net/qq_41305266/article/details/80991687

一、两次MD5

1. 用户端: PASS = MD5( 明文 + 固定 Salt)

2. 服务端: PASS = MD5( 用户输入 + 随机 Salt)

通过两次MD5,可以增大http明文传输过程或数据库被盗后,黑客通过彩虹表等手段反推出明文密码的难度(有一定作用,但不能保证绝对安全)。

pom文件添加依赖

<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
添加md5加密工具类

package com.wings.seckill.util;

import org.apache.commons.codec.digest.DigestUtils;

public class Md5Util {

private static final String SALT = "1a2b3c4d";

public static String md5(String src){
return DigestUtils.md5Hex(src);
}

public static String inputPass2FormPass(String inputPass){
String src = "" + SALT.charAt(0) + SALT.charAt(2)+ inputPass + SALT.charAt(5)+ SALT.charAt(4);
return md5(src);
}

public static String formPass2DbPass(String formPass, String salt){
String src = "" + salt.charAt(0) + salt.charAt(2)+ formPass + salt.charAt(5)+ salt.charAt(4);
return md5(src);
}

public static String inputPass2DbPass(String inputPass, String salt){
String formPass = inputPass2FormPass(inputPass);
String dbPass = formPass2DbPass(formPass, salt);
return dbPass;
}

public static void main(String[] args) {
String inputPass = "13632481101";
String salt = "mysalt";
String formPass = inputPass2FormPass(inputPass);
String dbPass1 = formPass2DbPass(formPass, salt);
String dbPass2 = inputPass2DbPass(inputPass, salt);
System.out.println(formPass);
System.out.println(dbPass1);
System.out.println(dbPass2);
}
}

新增用户表seckill_user

CREATE TABLE `seckill`.`Untitled` (
`id` bigint(20) NOT NULL COMMENT '用户ID,电话号码',
`nickname` varchar(255) NOT NULL,
`password` varchar(32) DEFAULT null COMMENT 'MD5(MD5(pass明文+固定salt)+salt)',
`salt` varchar(10) DEFAULT NULL,
`head` varchar(128) DEFAULT NULL COMMENT '头像,云存储的id',
`register_date` datetime(0) DEFAULT NULL COMMENT '注册时间',
`last_login_date` datetime(0) COMMENT '上次登录时间',
`login_count` int(11) DEFAULT 0 COMMENT '登录次数',
PRIMARY KEY (`id`)
);
添加电话号码校验工具类

package com.wings.seckill.util;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.alibaba.druid.util.StringUtils;

public class ValidatorUtil {

private static Pattern MOBILE_PATTERN = Pattern.compile("1\\d{10}");

public static boolean isMobile(String mobile){
if(StringUtils.isEmpty(mobile)){
return false;
}

Matcher matcher = MOBILE_PATTERN.matcher(mobile);
return matcher.matches();
}

public static void main(String[] args) {
boolean result1 = isMobile("13632481101");
boolean result2 = isMobile("1363248110");
System.out.println(result1);
System.out.println(result2);
}
}
SeckillUser domain、dao、service、controller

package com.wings.seckill.domain;

import java.util.Date;

public class SeckillUser {
private Long id;
private String nickname;
private String password;
private String salt;
private String head;
private Date registerDate;
private Date lastLoginDate;
private Integer loginCount;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
public Date getRegisterDate() {
return registerDate;
}
public void setRegisterDate(Date registerDate) {
this.registerDate = registerDate;
}
public Date getLastLoginDate() {
return lastLoginDate;
}
public void setLastLoginDate(Date lastLoginDate) {
this.lastLoginDate = lastLoginDate;
}
public Integer getLoginCount() {
return loginCount;
}
public void setLoginCount(Integer loginCount) {
this.loginCount = loginCount;
}
}
package com.wings.seckill.dao;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import com.wings.seckill.domain.SeckillUser;

@Mapper
public interface SeckillUserDao {

@Select("select * from seckill_user where id = #{id}")
public SeckillUser getById(long id);
}
package com.wings.seckill.service;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.wings.seckill.dao.SeckillUserDao;
import com.wings.seckill.domain.SeckillUser;
import com.wings.seckill.result.CodeMsg;
import com.wings.seckill.result.Result;
import com.wings.seckill.util.Md5Util;
import com.wings.seckill.util.ValidatorUtil;
import com.wings.seckill.vo.LoginVo;

@Service
public class SeckillUserService {

@Autowired
SeckillUserDao seckillUserDao;

public SeckillUser getById(long id){
return seckillUserDao.getById(id);
}

public CodeMsg login(LoginVo loginVo){
if(loginVo == null){
return CodeMsg.SERVER_ERROR;
}
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();

if(StringUtils.isEmpty(mobile)){
return CodeMsg.MOBILE_EMPTY;
}
if(StringUtils.isEmpty(password)){
return CodeMsg.PASSWORD_EMPTY;
}

if(!ValidatorUtil.isMobile(mobile)){
return CodeMsg.MOBILE_ERROR;
}

SeckillUser user = seckillUserDao.getById(Long.parseLong(loginVo.getMobile()));
if(user == null){
return CodeMsg.MOBILE_NOT_EXIST;
}

String salt = user.getSalt();
String dbPass = user.getPassword();
String md5Pass = Md5Util.formPass2DbPass(password, salt);
if(!dbPass.equals(md5Pass)){
return CodeMsg.PASSWORD_ERROR;
}

return CodeMsg.SUCCESS;
}
}
package com.imooc.miaosha.controller;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.imooc.miaosha.redis.RedisService;
import com.imooc.miaosha.result.Result;
import com.imooc.miaosha.service.MiaoshaUserService;
import com.imooc.miaosha.vo.LoginVo;

@Controller
@RequestMapping("/login")
public class LoginController {

private static Logger log = LoggerFactory.getLogger(LoginController.class);

@Autowired
MiaoshaUserService userService;

@Autowired
RedisService redisService;

@RequestMapping("/to_login")
public String toLogin() {
return "login";
}

@RequestMapping("/do_login")
@ResponseBody
public Result<Boolean> doLogin(HttpServletResponse response, @Valid LoginVo loginVo) {
log.info(loginVo.toString());
//登录
userService.login(response, loginVo);
return Result.success(true);
}
}
视图层增加如下文件

登陆页面:login.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>登录</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<!-- jquery -->
<script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>
<!-- bootstrap -->
<link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}" />
<script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script>
<!-- jquery-validator -->
<script type="text/javascript" th:src="@{/jquery-validation/jquery.validate.min.js}"></script>
<script type="text/javascript" th:src="@{/jquery-validation/localization/messages_zh.min.js}"></script>
<!-- layer -->
<script type="text/javascript" th:src="@{/layer/layer.js}"></script>
<!-- md5.js -->
<script type="text/javascript" th:src="@{/js/md5.min.js}"></script>
<!-- common.js -->
<script type="text/javascript" th:src="@{/js/common.js}"></script>

</head>
<body>

<form name="loginForm" id="loginForm" method="post" style="width:50%; margin:0 auto">

<h2 style="text-align:center; margin-bottom: 20px">用户登录</h2>

<div class="form-group">
<div class="row">
<label class="form-label col-md-4">请输入手机号码</label>
<div class="col-md-5">
<input id="mobile" name = "mobile" class="form-control" type="text" placeholder="手机号码" required="true" minlength="11" maxlength="11" />
</div>
<div class="col-md-1">
</div>
</div>
</div>

<div class="form-group">
<div class="row">
<label class="form-label col-md-4">请输入密码</label>
<div class="col-md-5">
<input id="password" name="password" class="form-control" type="password" placeholder="密码" required="true" minlength="6" maxlength="16" />
</div>
</div>
</div>

<div class="row">
<div class="col-md-5">
<button class="btn btn-primary btn-block" type="reset" onclick="reset()">重置</button>
</div>
<div class="col-md-5">
<button class="btn btn-primary btn-block" type="submit" onclick="login()">登录</button>
</div>
</div>

</form>
</body>
<script>
function login(){
$("#loginForm").validate({
submitHandler:function(form){
doLogin();
}
});
}
function doLogin(){
g_showLoading();

var inputPass = $("#password").val();
var salt = g_passsword_salt;
var str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) + salt.charAt(4);
var password = md5(str);

$.ajax({
url: "/login/do_login",
type: "POST",
data:{
mobile:$("#mobile").val(),
password: password
},
success:function(data){
layer.closeAll();
if(data.code == 0){
layer.msg("成功");
}else{
layer.msg(data.msg);
}
},
error:function(){
layer.closeAll();
}
});
}
</script>
</html>
全局自定义 common.js

//展示loading
function g_showLoading(){
var idx = layer.msg('处理中...', {icon: 16,shade: [0.5, '#f5f5f5'],scrollbar: false,offset: '0px', time:100000}) ;
return idx;
}
//salt
var g_passsword_salt="1a2b3c4d"

(二)JSR303参数校验 + 全局异常处理器

service中的方法入参有许多参数的判断代码,为了简化,可以利用JSR303参数校验

pom文件引入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
自定义参数校验注解

package com.wings.seckill.validator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.apache.commons.lang3.StringUtils;

import com.wings.seckill.util.ValidatorUtil;

public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {

private boolean required;

@Override
public void initialize(IsMobile isMobile) {
required = isMobile.required();
}

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if(!required && StringUtils.isEmpty(value)){
return true;
}
return ValidatorUtil.isMobile(value);
}
}
package com.wings.seckill.validator;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = IsMobileValidator.class )
public @interface IsMobile {

boolean required() default true;

String message() default "手机号码格式不正确";

Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };
}
使用该自定义注解:

package com.wings.seckill.vo;

import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.Length;

import com.wings.seckill.validator.IsMobile;

public class LoginVo {

@NotNull
@IsMobile
private String mobile;
@NotNull
@Length(min = 32)
private String password;

public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "LoginVo [mobile=" + mobile + ", password=" + password + "]";
}
}
在对应方法中加入@Valid 开启参数校验

@RequestMapping("/do_login")
@ResponseBody
public Result<CodeMsg> doLogin(@Valid LoginVo loginVo) {
log.info(loginVo.toString());
seckillUserService.login(loginVo);
return Result.success(CodeMsg.SUCCESS);
}
service的login方法简化如下:

public boolean login(LoginVo loginVo){
if(loginVo == null){
//return CodeMsg.SERVER_ERROR;
throw new GlobalException(CodeMsg.SERVER_ERROR);
}
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
/*
if(StringUtils.isEmpty(mobile)){
return CodeMsg.MOBILE_EMPTY;
}
if(StringUtils.isEmpty(password)){
return CodeMsg.PASSWORD_EMPTY;
}

if(!ValidatorUtil.isMobile(mobile)){
return CodeMsg.MOBILE_ERROR;
}
*/
SeckillUser user = seckillUserDao.getById(Long.parseLong(mobile));
if(user == null){
//return CodeMsg.MOBILE_NOT_EXIST;
throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
}

String salt = user.getSalt();
String dbPass = user.getPassword();
String md5Pass = Md5Util.formPass2DbPass(password, salt);
if(!dbPass.equals(md5Pass)){
//return CodeMsg.PASSWORD_ERROR;
throw new GlobalException(CodeMsg.PASSWORD_ERROR);
}

return true;
}
定义全局异常及异常处理器

package com.wings.seckill.exception;

import com.wings.seckill.result.CodeMsg;

public class GlobalException extends RuntimeException{

private static final long serialVersionUID = 31665074385012932L;
private CodeMsg cm;

public GlobalException(CodeMsg cm){
this.cm = cm;
}
public CodeMsg getCm() {
return cm;
}

}
package com.wings.seckill.exception;

import javax.servlet.http.HttpServletRequest;

import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.wings.seckill.result.CodeMsg;
import com.wings.seckill.result.Result;

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

@ExceptionHandler(value = Exception.class)
public Result<String> handleException(HttpServletRequest request, Exception ex){
ex.printStackTrace();

if(ex instanceof GlobalException){
GlobalException gex = (GlobalException)ex;
return Result.error(gex.getCm());
} else if(ex instanceof BindException){
BindException bex = (BindException)ex;
String message = bex.getAllErrors().get(0).getDefaultMessage();
return Result.error(CodeMsg.BIND_ERROR.fillMsg(message));
} else {
return Result.error(CodeMsg.SERVER_ERROR);
}
}
}
(三)分布式session

添加uuid工具类

package com.wings.seckill.util;

import java.util.UUID;

public class UUIDUtil {

public static String uuid(){
return UUID.randomUUID().toString().replace("-", "");
}
}
SeckillUserService的login方法添加以下代码

String token = UUIDUtil.uuid();
redisService.set(SeckillUserKey.token, token, user);
Cookie cookie = new Cookie(COOKIE_TOKEN_NAME, token);
cookie.setMaxAge(SeckillUserKey.token.expireSeconds());
cookie.setPath("/");

response.addCookie(cookie);
SeckillUserService添加通过token获取user对象方法

public SeckillUser getByToke(String token) {
if(StringUtils.isEmpty(token)){
return null;
}

return redisService.get(SeckillUserKey.token, token, SeckillUser.class);
}

登录成功后跳转到商品列表,对应的Controller及页面如下

package com.wings.seckill.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.wings.seckill.domain.SeckillUser;
import com.wings.seckill.service.SeckillUserService;

@Controller
@RequestMapping("/goods")
public class GoodsController {

private static Logger log = LoggerFactory.getLogger(GoodsController.class);

@Autowired
private SeckillUserService seckillUserService;

@RequestMapping("/to_list")
public String toList(Model model,
@CookieValue(name = SeckillUserService.COOKIE_TOKEN_NAME, required = false) String cookieToken,
// @RequestParam 是为了兼容默写手机端会把cookie信息放入请求参数中
@RequestParam(name = SeckillUserService.COOKIE_TOKEN_NAME, required = false) String paramToken) {

if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)){
return "/login/to_login";
}

String token = StringUtils.isEmpty(paramToken) ? cookieToken : paramToken;
SeckillUser seckillUser = seckillUserService.getByToke(token);
model.addAttribute("user", seckillUser);
return "goods_list";
}

}
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>商品列表</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'hello:'+${user.nickname}" ></p>
</body>
</html>
(四)优化

像User这种,几乎每个Controller方法都要使用的对象,如果每个Controller的方法都通过以上方式获取,代码将相当臃肿。这时,可以使用SpringMVC中WebMvcConfigurerAdapter的addArgumentResolvers,向需要user对象的方法进行入参注入。

package com.wings.seckill.config;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter{

@Autowired
private UserArgumentResolver userArgumentResolver;

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(userArgumentResolver);
}
}
package com.wings.seckill.config;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import com.wings.seckill.domain.SeckillUser;
import com.wings.seckill.service.SeckillUserService;

@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver{

@Autowired
private SeckillUserService seckillUserService;

@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> clazz = parameter.getParameterType();
return clazz == SeckillUser.class;
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);

String paramToken = request.getParameter(SeckillUserService.COOKIE_TOKEN_NAME);
String cookieToken = getCookieValue(request, SeckillUserService.COOKIE_TOKEN_NAME);

if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)){
return null;
}

String token = StringUtils.isEmpty(paramToken) ? cookieToken : paramToken;
return seckillUserService.getByToke(token, response);
}

private String getCookieValue(HttpServletRequest request, String cookieName) {
Cookie[] cookies = request.getCookies();
if(cookies != null){
for(Cookie cookie : cookies){
if(cookie.getName().equals(cookieName)){
return cookie.getValue();
}
}
}
return null;
}

}
package com.wings.seckill.service;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.wings.seckill.dao.SeckillUserDao;
import com.wings.seckill.domain.SeckillUser;
import com.wings.seckill.exception.GlobalException;
import com.wings.seckill.redis.RedisService;
import com.wings.seckill.redis.SeckillUserKey;
import com.wings.seckill.result.CodeMsg;
import com.wings.seckill.util.Md5Util;
import com.wings.seckill.util.UUIDUtil;
import com.wings.seckill.vo.LoginVo;

@Service
public class SeckillUserService {

public static final String COOKIE_TOKEN_NAME = "token";

@Autowired
SeckillUserDao seckillUserDao;

@Autowired
RedisService redisService;

public SeckillUser getById(long id){
return seckillUserDao.getById(id);
}

public boolean login(HttpServletResponse response, LoginVo loginVo){
if(loginVo == null){
throw new GlobalException(CodeMsg.SERVER_ERROR);
}
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();

SeckillUser user = seckillUserDao.getById(Long.parseLong(mobile));
if(user == null){
throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
}

String salt = user.getSalt();
String dbPass = user.getPassword();
String md5Pass = Md5Util.formPass2DbPass(password, salt);
if(!dbPass.equals(md5Pass)){
throw new GlobalException(CodeMsg.PASSWORD_ERROR);
}

String token = UUIDUtil.uuid();
addCookie(token, response, user);
return true;
}

public SeckillUser getByToke(String token, HttpServletResponse response) {
if(StringUtils.isEmpty(token)){
return null;
}

// 延长有效期
SeckillUser user = redisService.get(SeckillUserKey.token, token, SeckillUser.class);
if(user != null){
addCookie(token, response, user);
}
return user;
}

private void addCookie(String token, HttpServletResponse response, SeckillUser user){
redisService.set(SeckillUserKey.token, token, user);
Cookie cookie = new Cookie(COOKIE_TOKEN_NAME, token);
cookie.setMaxAge(SeckillUserKey.token.expireSeconds());
cookie.setPath("/");
response.addCookie(cookie);
}
}
这时Controller将会相当简洁

package com.wings.seckill.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.wings.seckill.domain.SeckillUser;

@Controller
@RequestMapping("/goods")
public class GoodsController {

@RequestMapping("/to_list")
public String toList(Model model, SeckillUser seckillUser) {
model.addAttribute("user", seckillUser);
return "goods_list";
}

}
 
---------------------
作者:插上小翅膀的程序猿Wings
来源:CSDN
原文:https://blog.csdn.net/qq_41305266/article/details/80991687
版权声明:本文为博主原创文章,转载请附上博文链接!

Java秒杀实战 (三)秒杀基本功能开发的更多相关文章

  1. WEB开发框架系列教程 (三)页面功能开发(2)

    上一节介绍了,基础资料币别信息的开发,只通过辅助开发工具,创建及资料表,填写 表名,程序就完全好了. 最后也说到,可能我们也会面对另外一些基础资料信息的维护,但是不是简单到只有代码 和名称,可能还有另 ...

  2. EBS OAF开发中的Java 实体对象(Entity Object)验证功能补充

    EBS OAF开发中的Java 实体对象(Entity Object)验证功能补充 (版权声明,本人原创或者翻译的文章如需转载,如转载用于个人学习,请注明出处:否则请与本人联系,违者必究) EO理论上 ...

  3. 20135231 JAVA实验报告三:敏捷开发与XP实践

    ---恢复内容开始--- JAVA实验报告三:敏捷开发与XP实践 20135231 何佳 实验内容 1. XP基础 2. XP核心实践 3. 相关工具 实验要求 1.没有Linux基础的同学建议先学习 ...

  4. 《Java 8实战》读书笔记系列——第三部分:高效Java 8编程(四):使用新的日期时间API

    https://www.lilu.org.cn/https://www.lilu.org.cn/ 第十二章:新的日期时间API 在Java 8之前,我们常用的日期时间API是java.util.Dat ...

  5. Java进阶(三)多线程开发关键技术

    原创文章,同步发自作者个人博客,转载请务必以超链接形式在文章开头处注明出处http://www.jasongj.com/java/multi_thread/. sleep和wait到底什么区别 其实这 ...

  6. “全栈2019”Java第三章:安装开发工具IntelliJ IDEA

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  7. go语言实战教程之 后台管理页面统计功能开发(1)

    本节内容我们将学习开发实现后台管理平台页面统计功能开发的功能接口,本章节内容将涉及到多种请求路由的方式. 功能介绍 后台管理平台不仅是功能管理平台,同时还是数据管理平台.从数据管理平台角度来说,在管理 ...

  8. FastAPI(七十)实战开发《在线课程学习系统》接口开发--留言功能开发

    在之前的文章:FastAPI(六十九)实战开发<在线课程学习系统>接口开发--修改密码,这次分享留言功能开发 我们能梳理下对应的逻辑 1.校验用户是否登录 2.校验留言的用户是否存在 3. ...

  9. go语言实战教程之 后台管理页面统计功能开发(2)

    上节内容介绍了后台管理页面统计功能开发(1),从功能介绍,到接口请求分析和归类,最后是代码设计.经过上节内容的介绍,已经将业务逻辑和开发逻辑解释清楚,本节内容侧重于编程代码实现具体的功能. 当日增长数 ...

  10. 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_5-2.微信扫一扫功能开发前期准备

    笔记 2.微信扫一扫功能开发前期准备         简介:讲解微信扫一扫功能相关开发流程和资料准备              1.微信开放平台介绍(申请里面的网站应用需要企业资料)          ...

随机推荐

  1. Jetson TK下如何写汇编语言

    首先,可以根据http://www.cnblogs.com/zenny-chen/p/3816620.html来安装CUDA工具链.这个工具集里包含了CUDA编译器以及其它必要的工具.然后,我们进入/ ...

  2. Eclipse添加Android library 错误的原因

    这两天把项目从本地转移到GIT上,本来我的Workspace是在D盘,现在因为感觉D盘不够用,就把GIT到的项目放到E盘了 按照以往的用法,GIT下来以后再往属性里添加依赖库就OK了,但是这次怎么也无 ...

  3. 使用badusb“烧鹅”制作“百度U盘”

    HID攻击:USB HID攻击技术是一种利用USB接口伪造用户击键行为实施是攻击的方式.通过恶意USB HID设备连接主机后发送伪造的按键命令,篡改系统设置.运行恶意功能.这种技术区别于传统的USB攻 ...

  4. 阶段5 3.微服务项目【学成在线】_day02 CMS前端开发_02-vuejs研究-vuejs基础-MVVM模式

    1.2.1 MVVM模式 vue.js是一个MVVM的框架,理解MVVM有利于学习vue.js.   MVVM拆分解释为:   Model:负责数据存储 View:负责页面展示 View Model: ...

  5. ios -转载-真机提示 iPhone has denied the launch request 问题

    环境: 手机版本12.1,Xcode10.0问题: 真机时提示 iPhone has denied the launch request ,试过了的各种方法,最终解决方法如下:1. 2. 3.清理Xc ...

  6. 源码搭建zabbix服务

    1) 部署LNMP 1.1) cd /root tar -xf lnmp_soft.tar.gz cd lnmp_soft/ tar -xf nginx-1.10.3.tar.gz cd nginx- ...

  7. 在vue项目中获取当前城市

    在vue项目中使用百度地图获取当前城市:https://www.jianshu.com/p/0819cfd46712 Vue2 :百度地图bmap:https://www.jianshu.com/p/ ...

  8. [转帖]centos7上设置中文字符集

    centos7上设置中文字符集 https://www.cnblogs.com/kaishirenshi/p/10528034.html author: headsen  chen date: 201 ...

  9. 关于@JsonFormat(出参格式化)和@DateTimeFormat(入参格式化)

    背景: 从数据库查询获取数据时候  返回的json数据 日期会出现一串数字或者其他形式  和我们期待的不一样 如下图: 一开始使用@DateTimeFormat注解 但是输出结果和没有使用返回的jso ...

  10. C++ 继承 - 在派生类中对基类初始化

    构造函数与基类的其他成员不同,不能被派生类继承,因此为了初始化基类中的成员变量,需要在派生类中调用基类的构造函数(即显式调用),如果派送类没有调用则默认调用基类的无参构造函数(即隐式调用). 显式调用 ...