转自: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. 阶段5 3.微服务项目【学成在线】_day04 页面静态化_15-页面静态化-模板管理-模板管理业务流程

    在视频教学的过程中,不会去做模板管理的模块 cms_template用来存储模板信息 cms_page 这一些课程页面用的是一个模板 模板的详情.templateFileId是模板的文件id 模板的文 ...

  2. layer弹框层学习笔记

    这里对layer的笔记只是大概记录一下其使用过程,以便后续使用时快速回顾,更详细使用及介绍参考官网实例.链接在本文末 一 .初步了解layer-弹层之美 layer是一款近年来备受青睐的web弹层组件 ...

  3. django项目添加路由----返回给客户端内容-----windows中的python

    django项目添加路由 url函数的第一个参数是匹配url路径的正则表达式,第2个参数是路由函数 第一个正则表达式是r'^$',其中r表示正则表达式字符串不对转义符进行转义.“^”表示匹配URL路径 ...

  4. 【ARTS】01_22_左耳听风-201900408~2019004014

    ARTS: Algrothm: leetcode算法题目 Review: 阅读并且点评一篇英文技术文章 Tip/Techni: 学习一个技术技巧 Share: 分享一篇有观点和思考的技术文章 Algo ...

  5. 推荐一个好用的免费开源的笔记本软件CherryTree

    我是一个好奇心很强的人,对未知的事物总有一种想要追根究底的冲动.多年以来,我学了很多东西,也学的很杂,积累了很多领域的知识.但不得不承认,人的记忆力很有限,学的越多忘的就越多.很久以前我就在想,怎么样 ...

  6. c++学习笔记_3

    前言:本笔记所对应的课程为中国大学mooc中北京大学的程序设计与算法(三)C++面向对象程序设计,主要供自己复习使用,且本笔记建立在会使用c和java的基础上,只针对与c和java的不同来写 类和对象 ...

  7. 在图中寻找最短路径-----深度优先算法C++实现

    求从图中的任意一点(起点)到另一点(终点)的最短路径,最短距离: 图中有数字的点表示为图中的不同海拔的高地,不能通过:没有数字的点表示海拔为0,为平地可以通过: 这个是典型的求图中两点的最短路径:本例 ...

  8. 基于mxgraph.js开发的流程图组件

    1.fabric.js 在决定使用mxgraph.js开发流程图之前,尝试过用fabric.js来开发,结果发现并没有想象中的那么简单,而且用户体验非常差,下面是体验地址:workFlow直到遇到一个 ...

  9. rdbtool

    https://www.cnblogs.com/wjoyxt/p/10577361.html https://github.com/sripathikrishnan/redis-rdb-tools h ...

  10. C语言控制台软件制作

    本题要求你写个程序把给定的符号打印成沙漏的形状.例如给定17个“*”,要求按下列格式打印 ***** *** * *** ***** 所谓“沙漏形状”,是指每行输出奇数个符号:各行符号中心对齐:相邻两 ...