论坛项目- 2 登录退出模块

1、发送邮件

在这里插入图片描述

1.1 qq开启smtp

在这里插入图片描述

1.2 spring email

导入jar包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
    <version>2.1.5.RELEASE</version>
</dependency>

properties配置

# MailProperties
spring.mail.host=smtp.qq.com
spring.mail.port=465
spring.mail.username=nowcoder@sina.com  //邮箱账号
spring.mail.password=nowcoder123   //smtp授权码
spring.mail.protocol=smtps    //协议
spring.mail.properties.mail.smtp.ssl.enable=true

工具类

@Component
public class MailClient {

    private static final Logger logger = LoggerFactory.getLogger(MailClient.class);

    @Autowired
    private JavaMailSender mailSender;        //java核心组件JavaMailSender

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

    public void sendMail(String to, String subject, String content) {
        try {
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);
            mailSender.send(helper.getMimeMessage());
        } catch (MessagingException e) {
            logger.error("发送邮件失败:" + e.getMessage());
        }
    }

}
//测试代码
    @Autowired
    private MailClient mailClient;

    @Autowired
    private TemplateEngine templateEngine;

    @Test
    public void testTextMail() {
        mailClient.sendMail("**8*@163.com", "TEST", "Welcome.");
    }


邮件模板

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>邮件示例</title>
</head>
<body>
    <p>欢迎你, <span style="color:red;" th:text="${username}"></span>!</p>
</body>
</html>

2、注册功能

分为注册激活两个步

2.1 注册

  • 在注册页面填写信息(账号、密码、确认密码、邮箱),点击注册
  • 之后根据路由(http://localhost:8080/community/register)post提交,之后由相应的controller处理,调用userService判断
    • ①提交的表单是否为空
    • ②账号是否已存在
    • ③邮箱是否已注册
    • ④若以上都没,则将调用userMapper该用户存储在数据库中,status设为0,此时还没激活,
    • ⑤并给邮箱发送激活邮件,其中包含跳转路径(http://localhost:8080/community/activation/userId/activationCode)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Controller层

    @RequestMapping(path = "/register", method = RequestMethod.POST)
    public String register(Model model, User user){
        Map<String, Object> map = userService.register(user);
        if(map == null || map.isEmpty()){
            //注册成果,前端提示,并跳转到首页,需要激活
            model.addAttribute("msg", "注册成功,我们已经向您的邮箱发送了一封激活邮件,请尽快激活!");
            model.addAttribute("target", "/index");
            return "/site/operate-result";
        }else {
            //将消息发送给前端
            model.addAttribute("usernameMsg", map.get("usernameMsg"));
            model.addAttribute("passwordMsg", map.get("passwordMsg"));
            model.addAttribute("emailMsg", map.get("emailMsg"));
            return "/site/register";
        }
    }

Service层

    public Map<String, Object> register(User user){
        HashMap<String, Object> map = new HashMap<>();
        //空值进行判断处理
        if(user == null){
            throw new IllegalArgumentException("参数不能为空");
        }
        if(StringUtils.isBlank(user.getUsername())){
            //将信息返回给客户端
            map.put("usernameMsg", "账号不能为空");
            return map;
        }
        if(StringUtils.isBlank(user.getPassword())){
            //将信息返回给客户端
            map.put("userPasswordMsg", "密码不能为空");
            return map;
        }
        if(StringUtils.isBlank(user.getEmail())){
            //将信息返回给客户端
            map.put("emailMsg", "邮箱不能为空");
            return map;
        }

        //输入,业务逻辑判断
        //验证账号
        User u = userMapper.selectByName(user.getUsername());
        if(u != null){
            map.put("usernameMsg", "该账号以存在");
            return map;
        }
        //验证邮箱
        u = userMapper.selectByEmail(user.getEmail());
        if(u != null){
            map.put("emailMsg", "该邮箱以被注册");
            return map;
        }

        //用户注册
        //密码加密
        user.setSalt(CommunityUtil.generateUUID().substring(0,5));
        user.setPassword(CommunityUtil.md5(user.getPassword() + user.getSalt()));
        user.setType(0);
        user.setStatus(0);
        user.setActivationCode(CommunityUtil.generateUUID());
        user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
        user.setCreateTime(new Date());
        userMapper.insertUser(user);

        //发送激活邮件
        Context context = new Context();
        context.setVariable("email", user.getEmail());
        //http://localhost:8080/community/activation/userid/激活码
        String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();
        context.setVariable("url", url);
        //使用模板引擎
        String content = templateEngine.process("/mail/activation", context);
        mailClient.sendMail(user.getEmail(), "激活账号", content);
        return null;
    }

2.2 激活

  • 用户点击激活邮件里的路径,服务器匹配相应的controller处理用户请求,该controller调用service主要处理:
    • ①根据路径中的激活码与userId对应的激活码,返回激活成功、已激活、失败信息
    • ②controller根据相应返回值,进行页面跳转

Controller层

    @RequestMapping(path = "/activation/{userId}/{code}", method = RequestMethod.GET)
    public String activation(Model model, @PathVariable("userId") int userId, @PathVariable("code") String code){
        int activation = userService.activation(userId, code);
        if(activation == ACTIVATION_SUCCESS){
            model.addAttribute("msg", "激活成功");
            model.addAttribute("target", "/login");  //激活成功,跳转到登录页面
        }else if(activation == ACTIVATION_REPEAT){
            model.addAttribute("msg", "无效操作,该账号已被激活");
            model.addAttribute("target", "/index");
        }else if(activation == ACTIVATION_FAILURE){
            model.addAttribute("msg", "激活失败,您提供的激活码不正确!");
            model.addAttribute("target", "/index");
        }
        return "/site/operate-result";
    }

Service层

    //激活账号
    public int activation(int userId, String code){
        //用户输入激活码,检查是否与数据库中的一致
        User user = userMapper.selectById(userId);
        if(user.getStatus() == 1){
            //表示已经被激活
            return ACTIVATION_REPEAT;
        }else if(user.getActivationCode().equals(code)){
            //激活成功,将status改为1
            userMapper.updateStatus(userId, 1);
            return ACTIVATION_SUCCESS;
        }else{
            //激活失败
            return ACTIVATION_FAILURE;
        }
    }

3、登录

生成验证码

@Configuration
public class KaptchaConfig {

    @Bean
    public Producer kaptchaProducer() {
        Properties properties = new Properties();
        properties.setProperty("kaptcha.image.width", "100");
        properties.setProperty("kaptcha.image.height", "40");
        properties.setProperty("kaptcha.textproducer.font.size", "32");
        properties.setProperty("kaptcha.textproducer.font.color", "0,0,0");
        properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYAZ");
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");

        DefaultKaptcha kaptcha = new DefaultKaptcha();
        Config config = new Config(properties);
        kaptcha.setConfig(config);
        return kaptcha;
    }
}

Controller

    @RequestMapping(path = "/login", method = RequestMethod.POST)
    public String login(String username, String password, String code, boolean rememberme,
                        Model model, HttpSession session, HttpServletResponse response) {
        // 检查验证码
        String kaptcha = (String) session.getAttribute("kaptcha");
        if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {
            model.addAttribute("codeMsg", "验证码不正确!");
            return "/site/login";
        }

        // 检查账号,密码
        int expiredSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
        Map<String, Object> map = userService.login(username, password, expiredSeconds);
        if (map.containsKey("ticket")) {
            Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
            //整个项目都有效
            cookie.setPath(contextPath);
            cookie.setMaxAge(expiredSeconds);
            response.addCookie(cookie);
            return "redirect:/index";
        } else {
            model.addAttribute("usernameMsg", map.get("usernameMsg"));
            model.addAttribute("passwordMsg", map.get("passwordMsg"));
            return "/site/login";
        }
    }

Service

    //登录
    public Map<String, Object> login(String username, String password, int expiredSeconds) {
        Map<String, Object> map = new HashMap<>();

        // 空值处理
        if (StringUtils.isBlank(username)) {
            map.put("usernameMsg", "账号不能为空!");
            return map;
        }
        if (StringUtils.isBlank(password)) {
            map.put("passwordMsg", "密码不能为空!");
            return map;
        }

        // 验证账号
        User user = userMapper.selectByName(username);
        if (user == null) {
            map.put("usernameMsg", "该账号不存在!");
            return map;
        }

        // 验证状态
        if (user.getStatus() == 0) {
            map.put("usernameMsg", "该账号未激活!");
            return map;
        }

        // 验证密码
        password = CommunityUtil.md5(password + user.getSalt());
        if (!user.getPassword().equals(password)) {
            map.put("passwordMsg", "密码不正确!");
            return map;
        }

        // 生成登录凭证
        LoginTicket loginTicket = new LoginTicket();
        loginTicket.setUserId(user.getId());
        loginTicket.setTicket(CommunityUtil.generateUUID());
        loginTicket.setStatus(0);
        loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000));
        loginTicketMapper.insertLoginTicket(loginTicket);

        map.put("ticket", loginTicket.getTicket());
        return map;
    }

4、退出

Controller

@RequestMapping(path = "/logout", method = RequestMethod.GET)
    public String logout(@CookieValue("ticket") String ticket) {
        userService.logout(ticket);
        return "redirect:/login";
    }

Service

public void logout(String ticket) {
        loginTicketMapper.updateStatus(ticket, 1);
    }

5、显示登录信息

5.1 拦截器示例

1、定义拦截器,实现 HandlerInterceptor;
在这里插入图片描述

@Component
public class AlphaInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);

    // 在Controller之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.debug("preHandle: " + handler.toString());
        return true;
    }

    // 在Controller之后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.debug("postHandle: " + handler.toString());
    }

    // 在TemplateEngine之后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.debug("afterCompletion: " + handler.toString());
    }
}

2、配置拦截器,为它指定拦截、排除的路径;
3、配置 MVC 的时候要实现 WebMvcConfigurer 这个类才可以;
4、将拦截器注入;
5、重写 addInterceptors 方法

  • 将拦截器添加进来: registry.addInterceptor
  • 排除所有静态资源:registry.excludePathPatterns
  • 设置拦截哪些路径:registry.addPathPatterns

在这里插入图片描述

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private AlphaInterceptor alphaInterceptor;

    @Autowired
    private LoginTicketInterceptor loginTicketInterceptor;

    @Autowired
    private LoginRequiredInterceptor loginRequiredInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(alphaInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
                .addPathPatterns("/register", "/login");

        registry.addInterceptor(loginTicketInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");

        registry.addInterceptor(loginRequiredInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
    }

}

测试一下是否拦截成功,index 页面不拦截,登录、注册页面被拦截:
在这里插入图片描述

5.2 拦截器应用(使用线程隔离批量处理用户请求)

  • 在请求开始时查询登录用户
  • 在本次请求中持有用户数据
  • 在模板视图上显示用户数据
  • 在请求结束时清理用户数据

登录成功时
在这里插入图片描述

在这里插入图片描述

5.2.1 新建 LoginTicketInterceptor 拦截器

1、将从 cookie 中获取特定信息的方法包装成一个工具类;

public class CookieUtil {

    public static String getValue(HttpServletRequest request, String name) {
        if (request == null || name == null) {
            throw new IllegalArgumentException("参数为空!");
        }

        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(name)) {
                    return cookie.getValue();
                }
            }
        }

        return null;
    }

}

request.getCookies();返回的是所有 cookie 信息,我们要从中取我们想要的信息。
2、实现 prehandle 方法:

  • 通过 cookie 得到 ticket 信息,用户登录之后会重定向到首页,这时我们可以从第二次请求中获取用户信息;
  • 在 prehandle 中只能通过请求 request 来获取 cookie;
  • 如果 ticket 不为空,调用 UserService 层去获取该凭证对应的所有登录信息(UserService 层要加一个方法);
  • 检查凭证是否有效:LoginTicket 不为空,状态码为0,且有效时长没有超时的情况是有效的,调用 UserService 层查询 user 数据;
@Component
public class LoginTicketInterceptor implements HandlerInterceptor {

    @Autowired
    private UserService userService;

    @Autowired
    private HostHolder hostHolder;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从cookie中获取凭证  cookie从request传过来
        String ticket = CookieUtil.getValue(request, "ticket");

        if (ticket != null) {
            // 查询凭证
            LoginTicket loginTicket = userService.findLoginTicket(ticket);
            // 检查凭证是否有效
            if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {
                // 根据凭证查询用户
                User user = userService.findUserById(loginTicket.getUserId());
                // 在本次请求中持有用户
                hostHolder.setUser(user);
            }
        }

        return true;
    }

	//在thymeleaf模板引擎之前用user,所以需在引用模板引擎之前调用,放入postHandler
	//postHandler模板引擎之前调用
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        User user = hostHolder.getUser();
        if (user != null && modelAndView != null) {
            modelAndView.addObject("loginUser", user);
        }
    }

	//清理数据
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        hostHolder.clear();
    }
}

解释:

     - 线程隔离存放用户信息,实现 HostHolde r类,使用 ThreadLocal 模拟 session 存放用户信息;
     - ThreadLocal 以线程为 key 实现存取值;
     - 将 HostHolder 注入,调用其中的 setUser 方法,将用户信息存到某一线程对应的map容器中;
     - HostHolder可以持有用户信息,hostHolder.setUser(user);将当前数据存入当前线程对应的map里,只要该请求没有结束,该线程一直在,当请求处理完,服务器向浏览器做出响应之后,该线程被销毁
5.2.2 UserService层
public LoginTicket findLoginTicket(String ticket) {
        return loginTicketMapper.selectByTicket(ticket);
    }
5.2.3WebMVC拦截器
registry.addInterceptor(loginTicketInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
5.2.4 html页面
<ul class="navbar-nav mr-auto">
	<li class="nav-item ml-3 btn-group-vertical">
		<a class="nav-link" th:href="@{/index}">首页</a>
	</li>
	<li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser!=null}">
		<a class="nav-link position-relative" href="site/letter.html">消息<span class="badge badge-danger">12</span></a>
	</li>
	<li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser==null}">
		<a class="nav-link" th:href="@{/register}">注册</a>
	</li>
	<li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser==null}">
		<a class="nav-link" th:href="@{/login}">登录</a>
	</li>
	<li class="nav-item ml-3 btn-group-vertical dropdown" th:if="${loginUser!=null}">
		<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
			<img th:src="${loginUser.headerUrl}" class="rounded-circle" style="width:30px;"/>
		</a>
		<div class="dropdown-menu" aria-labelledby="navbarDropdown">
			<a class="dropdown-item text-center" href="site/profile.html">个人主页</a>
			<a class="dropdown-item text-center" href="site/setting.html">账号设置</a>
			<a class="dropdown-item text-center" th:href="@{/logout}">退出登录</a>
			<div class="dropdown-divider"></div>
			<span class="dropdown-item text-center text-secondary" th:utext="${loginUser.username}">nowcoder</span>
		</div>
	</li>
</ul>

5.2.5测试结果

1、登录之前
在这里插入图片描述
2、登录之后
在这里插入图片描述
3、退出登录
在这里插入图片描述

6、账号设置

6.1 上传头像

在这里插入图片描述

6.1.1 新建UserController类
    @RequestMapping(path = "/setting", method = RequestMethod.GET)
    public String getSettingPage() {
        return "/site/setting";
6.1.2 测试

在这里插入图片描述
在这里插入图片描述

6.1.3 配置资源上传的路径(上传头像)
community.path.upload=d:/work/data/upload

我们只在 Controller 中处理上传文件的逻辑,而不调用 service 层,因为 MultipartFile 是表现层 SpringMVC 的一个组件,不要在 service 层调用;

6.1.4 UserService 层

实现更新用户头像方法,即根据用户的 userId 更新用户头像的 url 地址;

public int updateHeader(int userId, String headerUrl) {
        return userMapper.updateHeader(userId, headerUrl);
    }
6.1.5 UserController 层(客户端到服务器)
  • 判断文件是否为空,或者文件格式是否正确;
  • 生成随机文件名: 使用生成随机字符串工具类生成 随机字符+后缀 作为文件名
  • 确定文件目录是否为空,为空则创建目录;
  • 确定文件存放的位置:新建一个 file,其中是上传路径+/+文件名字
  • 存储文件:使用 transferTo(File file) 方法传到服务器中;
  • 更新当前用户的头像的路径(web访问路径):http://localhost:8080/community/user/header/xxx.png, 调用 UserService 层更新数据库中用户头像的 url 地址;
  • 重定向到首页。
    @RequestMapping(path = "/upload", method = RequestMethod.POST)
    public String uploadHeader(MultipartFile headerImage, Model model) {
        if (headerImage == null) {
            model.addAttribute("error", "您还没有选择图片!");
            return "/site/setting";
        }

        String fileName = headerImage.getOriginalFilename();
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        if (StringUtils.isBlank(suffix)) {
            model.addAttribute("error", "文件的格式不正确!");
            return "/site/setting";
        }

        // 生成随机文件名
        fileName = CommunityUtil.generateUUID() + suffix;
        // 确定文件存放的路径
        File dest = new File(uploadPath + "/" + fileName);
        try {
            // 存储文件
            headerImage.transferTo(dest);
        } catch (IOException e) {
            logger.error("上传文件失败: " + e.getMessage());
            throw new RuntimeException("上传文件失败,服务器发生异常!", e);
        }

        // 更新当前用户的头像的路径(web访问路径)
        // http://localhost:8080/community/user/header/xxx.png
        User user = hostHolder.getUser();
        String headerUrl = domain + contextPath + "/user/header/" + fileName;
        userService.updateHeader(user.getId(), headerUrl);

        return "redirect:/index";
    }
6.1.6 UserController 层:获取头像(服务器响应给客户端)
    @RequestMapping(path = "/header/{fileName}", method = RequestMethod.GET)
    public void getHeader(@PathVariable("fileName") String fileName, HttpServletResponse response) {
        // 服务器存放路径
        fileName = uploadPath + "/" + fileName;
        // 文件后缀
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        // 响应图片
        response.setContentType("image/" + suffix);
        try (
                FileInputStream fis = new FileInputStream(fileName);
                OutputStream os = response.getOutputStream();
        ) {
            byte[] buffer = new byte[1024];
            int b = 0;
            while ((b = fis.read(buffer)) != -1) {
                os.write(buffer, 0, b);
            }
        } catch (IOException e) {
            logger.error("读取头像失败: " + e.getMessage());
        }
    }
6.1.7 测试页面

不上传头像(前端逻辑也过不去)
在这里插入图片描述
上传不带文件后缀的文件:
在这里插入图片描述
正确上传头像:
在这里插入图片描述

6.2 修改密码

6.2.1 UserService 层

实现修改密码功能:

  • 空值处理:输入旧密码不能为空,输入的新密码不能为空,输入的新密码的确认密码不能为空;
  • 验证状态:
    • 判断两次输入的新密码是否一致,验证输入的旧密码是否正确;
    • 将用户输入的旧密码使用 MD5 加密算法进行加密得到加密后的密码,将这个密码和当前用户的密码进行比较,如果不一致,代表输入的密码错误
  • 使登录凭证失效:
    • 调用 DAO 层修改登录凭证状态码为 1 ;
@Override
public Map<String, Object> updatePassword(User user,String oldPassword, String newPassword, String confirmPassword,String ticket) {
    Map<String, Object> map = new HashMap<>();
    //空值处理
    if (StringUtils.isBlank(user.getPassword())) {
        map.put("oldPasswordMsg", "请输入您原来的密码!");
        return map;
    }
    if (StringUtils.isBlank(newPassword)) {
        map.put("newPasswordMsg", "请输入您的新密码!");
        return map;
    }
    if (StringUtils.isBlank(confirmPassword)) {
        map.put("confirmPasswordMsg", "请再次输入您的新密码!");
        return map;
    }
    //两次输入的新密码是否一致
    if (!newPassword.equals(confirmPassword)) {
        map.put("confirmPasswordMsg", "两次输入密码不一致!");
        return map;
    }
    //验证旧密码是否正确,注册的时候在末尾加盐,这里也一样
    oldPassword = CommunityUtil.md5(oldPassword + user.getSalt());
    if (!user.getPassword().equals(oldPassword)){
        map.put("oldPasswordMsg", "您输入的密码不正确!");
        return map;
    }
    //修改密码
    newPassword = CommunityUtil.md5(newPassword + user.getSalt());
    userMapper.updatePassword(user.getId(),newPassword);
    //修改用户登录状态,修改密码之后让用户重新登陆,所以要将原来的登录状态改为退出
    loginTicketMapper.updateStatus(ticket, 1);

    return map;
}

6.2.2 UserController 层
  • 实现修改密码的功能:
  • 从当前线程获取用户信息
  • 调用 UserService 层进行密码的校验;
  • 根据返回的 map 集合信息判断要跳转的页面,并且显示错误信息,或者修改成功的信息。
//修改密码
@RequestMapping(path = "/update", method = RequestMethod.POST)
public String updatePassword(Model model, String oldPassword,
                             String newPassword, String confirmPassword,
                             @CookieValue("ticket") String ticket
) {
    User user = hostHolder.getUser();
    Map<String, Object> map = userService.updatePassword(user, oldPassword, newPassword, confirmPassword, ticket);
    if (map == null || map.isEmpty()) {
        model.addAttribute("msg", "密码修改成功,请重新登录!");
        model.addAttribute("target", "/login");
        return "site/operate-result";
    }else {
        model.addAttribute("oldPasswordMsg",map.get("oldPasswordMsg"));
        model.addAttribute("newPasswordMsg",map.get("newPasswordMsg"));
        model.addAttribute("confirmPasswordMsg",map.get("confirmPasswordMsg"));
        return "/site/setting";
    }
}

7、检查登录状态

在用户没有登录的情况下,拦截一些请求,防止知道访问路径的人可以通过手动修改访问路径去访问一下页面;
在这里插入图片描述
常用的元注解(主要定义前两个就可以了):
@Target:定义在类、方法、属性等上面
@Retention:编译时有效or运行时有效
@Document:生成文档的时候要不要带这个注解
@Inherited:子类要不要继承父类的这个注解

如何读取注解:
Method.getDeclaredAnnotations ():获取方法上的所有注解
Method.getAnnotation (Class annotationClass):尝试获取某个类型的注解

7.1 在 annotation 包下新建 LoginRequired 类

1、其中定义一个登录之后才能访问的页面的注解
2、给所有登录后才能访问的方法,加上这个自定义注解(所有关于用户设置的,都要在方法上方加上这个注解,但是获取用户头像不用这个注解,因为在首页的时候要显示所有帖子的用户头像)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {

}

7.2 新建 LoginRequiredInterceptor 类

1、注入 Hostholder,获取当前是否有用户信息
2、如果拦截到的是一个方法的话,将他转型
3、从方法上面取对应的自定义注解类型的注解
4、如果获取到了该注解,但是获取到的用户为空,我们就进行拦截,即返回false,同时强制重定向到登录页面

@Component
public class LoginRequiredInterceptor implements HandlerInterceptor {
    @Autowired
    private HostHolder hostHolder;

    //在请求前拦截
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
            if (loginRequired != null && hostHolder.getUser() == null) {
                response.sendRedirect(request.getContextPath() + "/login");
                return false;
            }
        }
        return true;
    }
}

7.3 配置拦截器:

@Autowired
private LoginRequiredInterceptor loginRequiredInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(loginRequiredInterceptor)
            .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值