【在线网页收藏夹】后端

【在线网页收藏夹】后端

  • 本项目使用的技术
    • 后端框架:spring boot + JPA
    • web框架:spring MVC(Model模型、View视图、Controller控制器)
    • 前端框架:layUI
  • 包目录结构
    在这里插入图片描述

config(配置包)

WebMvcConfig.java (配置类,实现WebMvcConfigurer接口)
package com.favorites.config;

import com.favorites.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 登录拦截
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")                             //所有路径都被拦截
                .excludePathPatterns("/login/**", "/register/**",   //添加不被拦截的路径
                        "/login.html", "/layui/**", "/images/**",
                        "/favicon.ico");
    }
}
关于WebMvcConfigurer接口

WebMvcConfigurer是spring boot内部的一种配置方式

  • 实现WebMvcConfigurer接口(推荐)

  • 继承WebMvcConfigurationSupport类。

接口常用的方法:

 /* 拦截器配置 */
void addInterceptors(InterceptorRegistry var1);
/* 视图跳转控制器 */
void addViewControllers(ViewControllerRegistry registry);
/* 静态资源处理 */
void addResourceHandlers(ResourceHandlerRegistry registry);
代码分析

该类主要实现WebMvcConfigurer接口的拦截器addInterceptors方法,实现用户登录拦截。

public void addInterceptors(InterceptorRegistry registry) {
    super.addInterceptors(registry);
    registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**").excludePathPatterns("/emp/toLogin","/emp/login","/js/**","/css/**","/images/**");
}
  • addPathPatterns("/**")addPathPatterns()用于设置拦截器的过滤路径规则,/**表示对所有请求都拦截。
  • excludePathPatterns():用于设置不需要拦截的过滤路径规则。

更多接口相关方法参考博客:https://blog.csdn.net/zhangpower1993/article/details/89016503#2.%20WebMvcConfigurer%E6%8E%A5%E5%8F%A3

interceptor(拦截器包)

LoginInterceptor.java(登录拦截类)
package com.favorites.interceptor;

import com.favorites.dao.UserRepository;
import com.favorites.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Base64;

@Component
public class LoginInterceptor implements HandlerInterceptor {

    //base64解码器,解码使用基本型base64编码方案
    private Base64.Decoder decoder = Base64.getDecoder();

    @Autowired
    private UserRepository userRepository;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //统一拦截(查询当前session是否存在user)
        User user = (User) request.getSession().getAttribute("user");
        if (user != null) {
            return true;
        } else {
            // 查看cookie
            if (request.getCookies() != null) {
                for (Cookie cookie : request.getCookies()) {
                    if (cookie.getName().equals("token")) {
                        String[] token = new String(decoder.decode(cookie.getValue())).split("&&");
                        User user1 = userRepository.findByUsername(token[0]);
                        if (user1 != null && user1.getPassword().equals(token[1])) {
                            request.getSession().setAttribute("user", user1);
                            return true;
                        }
                    }
                }
            }
            response.sendRedirect("/login.html");
        }
        return false;
    }
}
关于HandlerInterceptor接口

HandlerInterceptor是spring处理器拦截器,主要功能

  • 用户是否登录及用户权限
  • 根据用户选择决定用HTML或Excel作为View
  • *blackboard building block的应用(简单理解)

接口常用方法:

public interface HandlerInterceptor {

   /**
     * 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
     * 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
   */
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;

   /**
     * 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
   */
    void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;

   /**
    * 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
   */
    void afterCompletion(
        	HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;
}
代码分析

该类主要使用preHandle()方法(预处理回调方法)。在进行登陆拦截前检查用户是否登录(检查sessioncookie中用户登录信息),若未登录则进行拦截返回falseresponse返回为"/login.html"登陆页面进行重新登录;若已登录,则返回true,继续执行下一步。

关于判断用户是否已登录
  • 首先统一拦截,查询session中是否有用户的账号,密码信息。(个人理解为已在登录框中输入信息)
  • 若输入框中无信息,则查询cookie中是否有用户的token,cookie中可储存两周内登陆过的用户信息。

model(模型包/实体类)

当model作为包名时,一般存的是实体类的模型,一般是给前端用于显示实体的字段内容。

User.java(用户实体类)
package com.favorites.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Data
@Entity
@Table(name = "t_user")
@org.hibernate.annotations.Table(appliesTo = "t_user", comment = "用户表")
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
public class User {

    @Id
    @Column(name = "id", nullable = false, columnDefinition = "int(10) comment '主键ID(自增)'")
    @GeneratedValue
    private Integer id;

    @Column(name = "username", unique = true, nullable = false, columnDefinition = "varchar(100) comment '用户名'")
    private String username;

    @Column(name = "password", nullable = false, columnDefinition = "varchar(100) comment '密码'")
    private String password;

    @Column(name = "email", unique = true, nullable = false, columnDefinition = "varchar(100) comment '邮箱'")
    private String email;

    @Column(name = "random_key", nullable = false, columnDefinition = "varchar(10) comment '随机key'")
    private String randomKey;
}

代码分析

使用hibernate框架对数据库进行操作,定义User模型时,建立用户表。

  • @Table(name="XX"):指明Table的名字
  • @Id@GeneratedValue:指定主键
  • @Column:Table的列名

Dao(数据接口访问层)

UserRepository.java(用户接口类)
package com.favorites.dao;

import com.favorites.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;


@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
    
    //通过用户名查询用户
    User findByUsername(String userName);
	
    //通过邮箱查询用户
    User findByEmail(String email);
	
    //通过用户名或邮箱查询用户
    User findByUsernameOrEmail(String username, String email);
	
    //通过用户名和邮箱查询用户
    User findByUsernameAndEmail(String username, String email);
}
关于JpaRepository接口

JPA是spring boot的数据库访问组件。

  • 使用前需要配置application.yml文件的数据库。
  • 使用 @Repository 注解
  • 继承 JPARepository
  • UserRepository 不需要编写任何代码,就可实现增删改查

service(数据服务接口层)

UserService.java(用户服务类)
package com.favorites.service;

import com.favorites.dao.UserRepository;
import com.favorites.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public User findByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    public User findByUsernameAndEmail(String username, String email) {
        return userRepository.findByUsernameAndEmail(username, email);
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public User findByUsernameOrEmail(String username, String email) {
        return userRepository.findByUsernameOrEmail(username, email);
    }

    public User findByEmail(String email) {
        return userRepository.findByEmail(email);
    }
}
代码分析

个人理解:UserService.java用户服务类的功能是实现UserRepository.java用户接口的方法。

Controller(前端控制层)

UserController.java(用户控制类)
package com.favorites.controller;

import com.favorites.model.User;
import com.favorites.service.UserService;
import com.favorites.util.ApiResponse;
import com.favorites.util.PasswordUtils;
import com.favorites.util.SpringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/info")
    //获取登录用户信息
    public ApiResponse info() {
        User user = (User) SpringUtils.getRequest().getSession().getAttribute("user");
        return ApiResponse.success(user);
    }

    @PostMapping("/password")
    //修改密码
    public ApiResponse password(String oldPassword, String newPassword) {
        User user = (User) SpringUtils.getRequest().getSession().getAttribute("user");
        if (user.getPassword().equals(DigestUtils.md5DigestAsHex((oldPassword + user.getRandomKey()).getBytes()))) {
            user.setRandomKey(PasswordUtils.randomPassword(10));
            user.setPassword(DigestUtils.md5DigestAsHex((newPassword + user.getRandomKey()).getBytes()));
            userService.save(user);
            return ApiResponse.success();
        } else {
            return ApiResponse.error("身份验证错误");
        }
    }
}

代码分析

@RequestMapping("/user"):映射处理请求,为控制器制定可以处理的URL请求。

使用方法:

  • 在类定义处,提供初步请求映射信息
  • 在方法处,提供详细映射信息
LoginController.java(登录控制类)
package com.favorites.controller;

import com.favorites.model.User;
import com.favorites.service.UserService;
import com.favorites.util.ApiResponse;
import com.favorites.util.EmailUtils;
import com.favorites.util.PasswordUtils;
import com.favorites.util.SpringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Base64;

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

    //base64编码器
    private final Base64.Encoder encoder = Base64.getEncoder();

    @Autowired
    private UserService userService;

    @Autowired
    private EmailUtils emailUtils;

    @PostMapping
    public ApiResponse login(@RequestBody User user, @RequestParam(required = false) String remember) {
        User user1 = userService.findByUsername(user.getUsername());
        if (user1 != null && user1.getPassword().equals(DigestUtils.md5DigestAsHex((user.getPassword() + user1.getRandomKey()).getBytes()))) {
            HttpServletRequest request = SpringUtils.getRequest();
            request.getSession().setAttribute("user", user1);
            //保存user1到session会话中,会话期间都有效
            if ("1".equals(remember)) {		//自动登录
                Cookie token = new Cookie("token", encoder.encodeToString((user1.getUsername() + "&&" + user1.getPassword()).getBytes()));
                token.setPath("/");
                token.setMaxAge(60 * 60 * 24 * 14);
                SpringUtils.getResponse().addCookie(token);
            }
            return ApiResponse.success();
        } else {
            return ApiResponse.error("用户名或密码错误");
        }
    }

    @PostMapping("/forgot")		//忘记密码
    public ApiResponse forgot(@RequestBody User user) {
        User user1 = userService.findByUsernameAndEmail(user.getUsername(), user.getEmail());
        if (user1 != null) {
            String tempPwd = PasswordUtils.randomPassword(8);
            // 重置用户密码
            user.setRandomKey(PasswordUtils.randomPassword(10));
            user.setPassword(DigestUtils.md5DigestAsHex((tempPwd + user.getRandomKey()).getBytes()));
            userService.save(user1);
            // 将临时密码发送至用户邮箱
            emailUtils.send(user1.getEmail(), "海星收藏夹|重置密码",
                    String.format("您的新密码:%s,请牢记。", tempPwd));
            return ApiResponse.success();
        } else {
            return ApiResponse.error("账号或邮箱不存在");
        }
    }

    @GetMapping("/out")
    //退出系统,清除session和cookie中的记录
    public ApiResponse logout() {
        HttpServletRequest request = SpringUtils.getRequest();
        request.getSession().removeAttribute("user");
        // 清除cookie
        if (request.getCookies() != null) {
            for (Cookie c : request.getCookies()) {
                Cookie cookie = new Cookie(c.getName(), null);
                cookie.setPath("/");
                cookie.setMaxAge(0);
                SpringUtils.getResponse().addCookie(cookie);
            }
        }
        return ApiResponse.success();
    }
}
代码分析
  • 登录功能:查询用户表,保存到session,自动登录
  • 忘记密码:用户名和邮箱查找用户,发送新密码邮件
  • 退出系统:清除session和cookie的数据
RegisterController.java(注册控制类)
package com.favorites.controller;

import com.favorites.model.Category;
import com.favorites.model.Favorites;
import com.favorites.model.User;
import com.favorites.service.CategoryService;
import com.favorites.service.FavoritesService;
import com.favorites.service.UserService;
import com.favorites.util.ApiResponse;
import com.favorites.util.PasswordUtils;
import com.favorites.util.PinYinUtils;
import com.favorites.util.SpringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/register")
public class RegisterController {

    @Autowired
    private UserService userService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private FavoritesService favoritesService;

    /**
     * 注册
     *
     * @param user
     * @return
     */
    @PostMapping
    public ApiResponse register(@RequestBody User user) {
        String password = user.getPassword();
        if (userService.findByUsernameOrEmail(user.getUsername(), user.getEmail()) == null) {
            user.setRandomKey(PasswordUtils.randomPassword(10));
            user.setPassword(DigestUtils.md5DigestAsHex((user.getPassword() + user.getRandomKey()).getBytes()));
            User user1 = userService.save(user);
            // 创建默认分类
            Category category = new Category(null, "默认分类", user1.getId(), 1, 9999, null);
            categoryService.save(category);
            // 推荐收藏
            List<Favorites> recommends = new ArrayList<>();
            recommends.add(new Favorites(null, "百度搜索", "https://www.baidu.com/favicon.ico", "https://www.baidu.com/", category.getId(), user1.getId(), PinYinUtils.toPinyin("百度搜索")));
            recommends.add(new Favorites(null, "谷歌翻译", "https://translate.google.cn/favicon.ico", "https://translate.google.cn/", category.getId(), user1.getId(), PinYinUtils.toPinyin("谷歌翻译")));
            favoritesService.saveAll(recommends);
            // 设置session
            SpringUtils.getRequest().getSession().setAttribute("user", user1);
            return ApiResponse.success();
        }
        return ApiResponse.error("用户名或邮箱已存在");
    }

    @GetMapping("/{username}")
    public ApiResponse checkUsername(@PathVariable String username) {
        User user = userService.findByUsername(username);
        if (user == null) {
            return ApiResponse.success();
        }
        return ApiResponse.error();
    }

    @GetMapping("/email/{email}")
    public ApiResponse checkEmail(@PathVariable String email) {
        User user = userService.findByEmail(email);
        if (user == null) {
            return ApiResponse.success();
        }
        return ApiResponse.error();
    }
}

util(工具包)

SpringUtils.java(spring工具类 响应和请求)
package com.favorites.util;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

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

public class SpringUtils {
    public static HttpServletRequest getRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static HttpServletResponse getResponse() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
    }
}
代码分析
  • HttpServletRequest对象:代表客户端的请求,通过这个对象提供的方法,可以获得客户端请求的所有信息。

  • HttpServletResponse对象:代表服务器的响应,这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。

ApiResponse.java(接口调用响应,成功/失败)
package com.favorites.util;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class ApiResponse {
    Integer code;
    String msg;
    Object data;

    public static ApiResponse success() {
        return new ApiResponse(0, "success", null);
    }

    public static ApiResponse success(Object data) {
        return new ApiResponse(0, "success", data);
    }

    public static ApiResponse error() {
        return new ApiResponse(-1, "error", null);
    }

    public static ApiResponse error(String msg) {
        return new ApiResponse(-1, msg, null);
    }
}
PasswordUtils.java(随机生成密码)
package com.favorites.util;

import java.util.Random;

public class PasswordUtils {

    private static final String SCOPE = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890";

    private static final Random R = new Random();

    /**
     * 生成随机密码
     *
     * @param length
     * @return
     */
    public static String randomPassword(int length) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(SCOPE.charAt(R.nextInt(SCOPE.length())));
        }
        return sb.toString();
    }
}
EmailUtils.java(邮箱工具类)
package com.favorites.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class EmailUtils {

    @Autowired
    private JavaMailSender javaMailSender;

    @Value("${spring.mail.username}")
    private String from;

    @Async
    public void send(String to, String title, String text) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setSubject(title);
        message.setText(text);
        message.setTo(to);
        message.setFrom(from);
        javaMailSender.send(message);
    }
}

JavaMailSender接口:实现邮件的发送

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值