Spring boot 论坛项目实战_02

Spring Boot 实践,开发社区登录模块

1.发送邮件

  • 邮箱设置

    • 启用客户端SMTP服务

    • 才用新浪邮箱,设置里面进行服务开启

  • Spring Email【集成在 Spring 中】

    • 导入 jar 包

      • <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
            <version>2.1.5.RELEASE</version>
        </dependency>
    • 邮箱参数配置

      • 用配置文件,完成邮箱参数配置,不要一次性写死,便于后续更新

      • Application.properties 中配置

      • # MailProperties    
        # 使用的邮箱域名
        spring.mail.host=smtp.sina.com
        spring.mail.port=465
        spring.mail.username= 自己的邮箱用户名
        spring.mail.password= 自己的邮箱的 SMTP 授权码
        # 指定协议
        spring.mail.protocol=smtps
        # 其他详尽配置
        # 发送邮件的时候采用安全的 ssl 连接
        spring.mail.properties.mail.smtp.ssl.enable=true
    • 使用 JavaMailSender[Spring 的 Bean] 发送邮件

      • 如果出现: 身份认证错误[ 下面这种 ], 记得检查邮箱配置的 username 和 password

      • org.springframework.mail.MailAuthenticationException: Authentication failed;

      注意: 用 STMP 发送的文件, 在发送邮箱本身的已发送是看不到的.

  • 模板引擎

    • 使用Thymeleaf 发送 HTML 邮件

2.开发注册功能

  • 访问注册页面

    • 点击顶部区域内的链接,打开注册页面

    •  

       

  • 提交注册数据

    • 通过表单提交数据

    • 服务端验证帐号是否已存在、邮箱是否已注册

    •  

       

    •  

       

      • 这里注意下,一定要保证数据库里的数据唯一性,不然会报错

    • 服务端发送激活邮件

  • 激活注册帐号

    • 点击邮件中的链接,访问服务端的激活服务

    •  

       

3.会话管理

  • HTTP的基本性质

    • HTTP是简单的

    • HTTP是可扩展的

    • HTTP是无状态的,有会话的

      • 使用 Cookies 可以创建有状态的会话

  • Cookie【分布式,尽量用Cookie保存数据】

    • 是服务器发送到浏览器,并保存在浏览器端的一小块数据。

    • 浏览器下次访问该服务器时,会自动携带该块数据,并将其发送给服务器

    • // Cookie 示例
      ​
          @RequestMapping(path = "/cookie/set", method = RequestMethod.GET)
          @ResponseBody
          public String setCookie(HttpServletResponse response) {
              // 创建Cookie
              Cookie cookie = new Cookie("code", CommunityUtil.generateUUID());
              // 设置 Cookie 生效范围
              cookie.setPath("/community/alpha");
              // 设置 Cookie 生存时间, 默认 关闭浏览器就消失
              cookie.setMaxAge(60 * 10);// 单位秒
              // 发送 cookie
              response.addCookie(cookie);
      ​
              return "set cookie";
          }
      ​
          @RequestMapping(path = "/cookie/get", method = RequestMethod.GET)
          @ResponseBody
          public String getCookie(@CookieValue("code") String code){
              System.out.println(code);
              return "get cookie";
          }
  • Session【分布式中不方便存 Cookie 的数据,可以存在 NoSql 数据库中:Redis】

    • 是 JavaEE 的标准【API】,用于在服务端记录客户端信息

    • 数据存放在服务端更加安全,但是也会增加服务端的内存压力

    • // Session 示例
          @RequestMapping(path ="/session/set" , method = RequestMethod.GET)
          @ResponseBody
          public String setSession(HttpSession session){
              session.setAttribute("id",1);
              session.setAttribute("name","test");
              return "set session";
          }
      ​
          @RequestMapping(path ="/session/get" , method = RequestMethod.GET)
          @ResponseBody
          public String getSession(HttpSession session){
              System.out.println(session.getAttribute("id"));
              System.out.println(session.getAttribute("name"));
              return "get session";
          }

 

4. 生成验证码

 

package com.nowcoder.community.config;
​
import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
import java.util.Properties;
​
/**
 * Kaptcha 的配置类
 */
​
@Configuration
public class KaptchaConfig {
    
    @Bean
    public Producer kaptchaProducer(){
        Properties properties = new Properties();
        // 设置 kaptcha 的属性
        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");// R,G,B
                                    properties.setProperty("kaptcha.textproducer.char.string","1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        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;
​
    }
​
}

 

#JS问题
<!-- 原本刷新验证码方法失效 -->
<a href="javascript:refresh_kaptcha();" class="font-size-12 align-bottom">刷新验证码</a>
<script>
  function refresh_kaptcha() {
         var path = CONTEXT_PATH + "/kaptcha?p=" + Math.random();
         $("#kaptcha").attr('src', path);
     }
</script><!-- 
    解决办法: 用点击实现验证码图片的 src 更新, 添加随机参数保证图片的刷新
-->
<a href="javascript:refresh_kaptcha();" onclick="changeImage()"  class="font-size-12 align-bottom">刷新验证码</a>
<script>
    function changeImage(){
            var element = document.getElementById('kaptcha');
            var path = CONTEXT_PATH + "/kaptcha?p=" + Math.random();
            element.src = path;
        }
</script>
 

5.开发登录、退出功能

  • 访问登录页面

    • 点击顶部区域内的链接,打开登录页面

  • 登录

    • 验证帐号、密码、验证码

    • 成功时,生成登录凭证,发放给客户端

    • 失败时,跳转回登录页

  • 退出

    • 将登陆凭证更新为失效状态

    • 跳转至网站首页

 

6. 显示登录信息

  • 拦截器示例

    • 定义拦截器,实现 HandlerInterceptor 接口

    • 配置拦截器,为它指定拦截、排除的路径

      • /**
         * 拦截器的配置类
         * implements WebMvcConfigurer
         * 实现这个接口
         */
        @Configuration
        public class WebMvcConfig implements WebMvcConfigurer {
        ​
            // 注入拦截器对象
            @Autowired
            private AlphaInterceptor alphaInterceptor;
        ​
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                // 添加要注册的拦截器;
                // 指定[排除]拦截路径, 若不指定则是拦截所有资源;
                // 明确要拦截的路径
                registry.addInterceptor(alphaInterceptor)
             /* 排除对静态资源的拦截*/           .excludePathPatterns("/**/*.css","/**/*.js","/**/*.png","/**/*.jpg","/**/*.jpeg")
                    /*指定要拦截的 路径*/
                        .addPathPatterns("/register","/login");
            }
        }
      • 实现:

        1. 写一个类实现接口,根据实际情况写方法

        2. 配置拦截器,指定它拦截的请求

      • 优势:降低代码耦合

  • 拦截器应用

    • 在请求开始时,查询登录用户

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

         

    • 在本次请求中持有用户数据

      • // 利用 ThreadLocal 保证多线程的并发安全: 
        // 	以线程[CurrentThread]为 Map 的key 来存取值
    • 在模板视图上显示用户数据

      • // 在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());
            }

         

7.账号设置

  • 上传文件

    • 请求:必须是POST 请求

    • 表单:添加属性 : enctype= “multipart/form-data”

    • Spring MVC:通过MultipartFile 处理上传文件

    • 注意需要先把上传路径的文件夹建好

  • 开发步骤

    • 访问帐号设置页面

    • 上传头像

    • 获取头像

 

8.检查登陆状态

  • 使用拦截器

    • 在方法前标注自定义注解

    • 拦截所有请求,只处理带有该注解的方法

  • 自定义注解

    • 注解的类型是:

    • 常用的元注解:

      • // 定义自己声明的注解,可以用在哪个位置
        @Target
        // 声明自定义注解,保留或有效时间
        @Retention
        // 【下面两个非必须】
        // 声明自定义注解,在生成文档的时候要不要带上这个注解
        @Document
        // 用于继承,子类在继承父类时,要不要继承这个自定义注解
        @Inherited
    • 如何读取注解:

      • // 通过反射读取, 调用注解
        // 获取所有注解
        Method.getDeclaredAnnotations();
        // 按照类型获取这个类型的注解
        Method.getAnnotation(Class<T> annotationClass);
    • 注解的使用:

      • /*
        * 这个注解的位置 : 方法上面
        * 这个注解的有效期: 程序运行时
        */
        @Target(ElementType.METHOD)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface LoginRequired {
            // 这个注解就只起一个标识的作用
            // 声明这个方法,要在程序运行后才能访问
        }
posted @ 2020-09-07 18:36  云川望雨  阅读(404)  评论(0编辑  收藏  举报