数据库的设计
有两张表,分别是文章表和用户表,文章表中包含了id,title,content,createtime,uid,rcount,stat,其中id是自增的。用户表中包含了id,title,username,password,phtot,createtime,updatetime,state,其中id是也是自增的。
-
统一数据返回
将返回的数据进行统一函数的处理,使得返回前端的数据格式是一致的。
-
public class AjaxResult { /** * 业务执行成功进行返回的方法 * @param data * @return */ public static HashMap<String, Object> success(Object data){ HashMap<String, Object> result = new HashMap<>(); result.put("code",200); result.put("msg",""); result.put("data", data); return result; } public static HashMap<String, Object> success(Object data, String msg){ HashMap<String, Object> result = new HashMap<>(); result.put("code",200); result.put("msg",msg); result.put("data", data); return result; } /** * 失败时返回数据格式 * @param code * @param msg * @return */ public static HashMap<String, Object> fail(int code, String msg){ HashMap<String, Object> result = new HashMap<>(); result.put("code",code); result.put("msg",msg); result.put("data", ""); return result; } public static HashMap<String, Object> fail(int code, String msg,Object data){ HashMap<String, Object> result = new HashMap<>(); result.put("code",code); result.put("msg",msg); result.put("data",data); return result; } }
统一数据返回时,和前端约定好了按照固定的格式返回。调用此方法,不管是什么类型,都是统一的数据格式返回。
@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if(body instanceof HashMap){
//本身已经封装好了
return body;
}
if(body instanceof String){
//返回是一个String
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(AjaxResult.success(body));
}
return AjaxResult.success(body);
}
-
拦截器
对于未登录访问个人栏等不安全的行为,我们应该实现拦截器来阻止,拦截器的实现和拦截器配置规则如下:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//通过session判断是否有用户的信息,方法是从session中取对象,如果存在对象,就说明已经登陆了
HttpSession session = request.getSession(false);//false作用是,在没找到session对象时,也不能创建一个session
if(session != null && session.getAttribute(Constant.SESSION_USERINFO_KEY) != null){
return true;
}else {
response.setStatus(401);//未登录,权限不够
return false;//登录失败
}
}
//注入拦截器
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 拦截所有请求(url地址)
.excludePathPatterns("/user/reg") // 排除注册接口(注册接口不拦截)
.excludePathPatterns("/user/login")
.excludePathPatterns("/user/getuser")
.excludePathPatterns("/user/getsess")
.excludePathPatterns("/user/isartbyme")
.excludePathPatterns("/getcaptcha")
.excludePathPatterns("/art/list")//分页页面
.excludePathPatterns("/art/detail")
.excludePathPatterns("/art/totalpage")
.excludePathPatterns("/art/update_rcount")
.excludePathPatterns("/comment/list")
.excludePathPatterns("/**/*.html")//任意路径下的html后缀可以放行
.excludePathPatterns("/css/**")
.excludePathPatterns("/editor.md/**")
.excludePathPatterns("/img/**")//img目录下所有的文件都放行
.excludePathPatterns("/image/**")
.excludePathPatterns("/js/**");
}
-
博客的基本操作
/**
* 用户控制器
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/reg")
public Object reg(String username, String password){
//1.非空校验,虽然前端校验了,但是后端请求不一定来自前端
if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)){
return AjaxResult.fail(-1, "非法参数请求");
}
//2.添加操作
int result = userService.add(username,
SecurityUtil.encrypt(password));//密码加盐
if(result == 1){
return AjaxResult.success(1,"添加成功");
}else {
return AjaxResult.fail(-1,"添加失败");
}
}
//我们约定1为成功,非1为登录失败
@RequestMapping("/login")
@Transactional
public int login(HttpServletRequest request, String username, String password){
//1.非空校验,虽然前端校验了,但是后端请求不一定来自前端
if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)){
return 0;
}
//2.查询操作
UserInfo userInfo = userService.getUserByName(username);
if(userInfo == null || userInfo.getId() <= 0){
return -1;
}else {
boolean result = SecurityUtil.decrypt(password,userInfo.getPassword()) ;
if(result) {
HttpSession session = request.getSession();//通过默认参数。得到session对象,使用sessionID来实现的,然后采用后面的操作
session.setAttribute(Constant.SESSION_USERINFO_KEY, userInfo);//将常量key值和userinfo对象添加
//虽然每个用户的session key想通,但是里面的sessionID是不一样的,这样可以来区分不同的用户
return 1;
}
}
return -1;
}
@RequestMapping("/myinfo")
public UserInfo myInfo(HttpServletRequest request){
HttpSession session = request.getSession(false);
//判断session有值,并且有登录的值
if(session != null &&
session.getAttribute(Constant.SESSION_USERINFO_KEY) != null){
return (UserInfo) session.getAttribute(Constant.SESSION_USERINFO_KEY);
}
return null;
}
/**
* 退出登录
* @param request
* @return
*/
@RequestMapping("/logout")
public boolean logout(HttpServletRequest request){
HttpSession session = request.getSession(false);
//判断session有值,并且有登录的值
if(session != null &&
session.getAttribute(Constant.SESSION_USERINFO_KEY) != null){
session.removeAttribute(Constant.SESSION_USERINFO_KEY);//移除当前登录
}
return true;
}
-
密码加盐加密
随机得到32位的盐值,用md5DigestAsHex函数加密盐值加原始密码,返回盐值加加密后的密码作为最终的加密密码。
-
/** * 加密 * @param password * @return */ public static String encrypt(String password) { // 随机盐值,每次的盐值都为固定的32位 String salt = UUID.randomUUID().toString().replace("-",""); // 最终密码(md5(随机盐值+密码)) String finalPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes()); return salt+finalPassword; }
加盐密码的校验
public static boolean decrypt(String password, String finalPassword) { //非空校验 if (!StringUtils.hasLength(password) || !StringUtils.hasLength(finalPassword)) { return false; } if (finalPassword.length() != 64) { return false; } //得到盐值 String salt = finalPassword.substring(0,32); //使用盐值,将待确认的密码生成最终密码 String securityPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes()); //使用盐值与最终密码进行对比 return finalPassword.equals(salt+securityPassword); }