• 登录认证功能:
      从业务流程而言,只有登录后才能访问其他的功能/页面
      从技术而言,访问的所有内容都是通过url请求向服务器获取的
      从技术而言,在不登录的情况,只要获得url,也可以向服务器访问正常登录才能访问的内容
      明显不合理
      就在每次访问非登录的功能时,都判断一下是否已经登录了。
      image.png
      此登录认证功能具有以下特点
      1. 具有一定的共用行,在许多业务之前进行认证
      2. 并不是此次请求的目的
      上述特点符合AOP(面向切面的编程)结构设计中的切面设计
      Filter就可以作为一个切面
      所以自定义过滤器实现登录认证的操作(明白要写一个过滤器,怎么写呢?)
      1. 拦截所有的请求 //配置的时候方便
      2. 放过不需要认证的请求
      3. 需要认证的请求参考登录成功时装入session中的 user对象。
      什么时候使用session
      当在一个请求中产生了一些数据需要在后面的其他请求中使用
      就需要将数据装入session

      请求中的数据默认存储在request区域中
      request中的数据只在一次请求响应中有效,多次请求无效

      session的缓存机制
      浏览器第一次请求会在服务器端产生一个session对象
      同时生成一个为一个sessionid
      配对存储(map)
      将sessionid装入cookie,响应给浏览器
      浏览器后续请求时,自动传递cookie(sessionid)
      从而多次请求获得相同的session,多次请求共享,缓存
      多次请求是否能获得相同的session,取决于传递的cookie中携带sessionid
      session是无状态,不安全。
      清除浏览器cookie就无法获得之前的session ```java //Token.java package com.duyi.qxgl07.domain;

    /**

    • 自动登录的令牌验证信息 */ public class Token { private String tokenid ; //uuid private User user ; private String ip ; private long start ;//生效时间戳 时间毫秒表示 private long end ; //失效时间戳 时间毫秒表示

      public String getTokenid() {

      1. return tokenid;

      } public void setTokenid(String tokenid) {

      1. this.tokenid = tokenid;

      } public User getUser() {

      1. return user;

      } public void setUser(User user) {

      1. this.user = user;

      } public String getIp() {

      1. return ip;

      } public void setIp(String ip) {

      1. this.ip = ip;

      } public long getStart() {

      1. return start;

      } public void setStart(long start) {

      1. this.start = start;

      } public long getEnd() {

      1. return end;

      } public void setEnd(long end) {

      1. this.end = end;

      } public Token(String tokenid, User user, String ip, long start, long end) {

      1. this.tokenid = tokenid;
      2. this.user = user;
      3. this.ip = ip;
      4. this.start = start;
      5. this.end = end;

      } public Token() {} } java //LoginView.java package com.duyi.qxgl.view;

    import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;

    //是一个用来动态拼装登录页面的servlet public class LoginView extends HttpServlet { / 一般4开头的都代表没找到 405 : 表示处理请求的方法没有找到 service可以处理get和post请求 doGet可以处理get请求 doPost可以处理post请求 / @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //拼页面 String f = req.getParameter(“f”); //服务器响应时使用utf-8处理 resp.setCharacterEncoding(“utf-8”); StringBuilder html = new StringBuilder(); html.append(““); html.append(“ “); html.append(“ “); //告诉浏览器显示时按照 utf-8处理 html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“

    “); html.append(“
    用户登录
    “); html.append(“
    “); html.append(“
    “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“ “); html.append(“
    用户名:“); html.append(“ “); html.append(“
    密码:
    1周内自动登录
    “); html.append(“
    “); html.append(“
    “); html.append(“
    “); html.append(“ “); html.append(““); //将网页直接响应回去 resp.getWriter().write(html.toString());

    1. /*
    2. servlet 和 jsp都可以拼装动态网页
    3. servlet = java(主场) + html
    4. jsp = java + html(主场)
    5. */
    6. }

    }

    1. ```java
    2. //LoginController.java
    3. package com.duyi.qxgl07.controller;
    4. import com.duyi.qxgl07.domain.Token;
    5. import com.duyi.qxgl07.domain.User;
    6. import com.duyi.qxgl07.service.UserService;
    7. import javax.servlet.ServletException;
    8. import javax.servlet.http.Cookie;
    9. import javax.servlet.http.HttpServlet;
    10. import javax.servlet.http.HttpServletRequest;
    11. import javax.servlet.http.HttpServletResponse;
    12. import java.io.IOException;
    13. import java.util.UUID;
    14. public class LoginController extends HttpServlet {
    15. @Override
    16. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    17. //验证此次登录的账号和密码是否正确
    18. //要想验证,需要先获得账号密码
    19. //如何获得账号密码
    20. //根据请求时传递的参数key,获得对应的参数值。 这个key就是网页中input.name值,获得的参数值就是网页中input输入的值
    21. String uname = req.getParameter("uname") ;
    22. String upass = req.getParameter("upass") ;
    23. String autoflag = req.getParameter("autoflag");
    24. //service查找
    25. UserService service = new UserService();
    26. User user = service.checkLogin(uname,upass);
    27. if(user == null){
    28. //根据账号密码,没有找到数据,登录失败,重新显示登录页面
    29. resp.sendRedirect("login.view?f=9");
    30. }else{
    31. //自动登录处理
    32. if(autoflag != null && !"".equals(autoflag)){
    33. //要实现自动登录
    34. String tokenId = UUID.randomUUID().toString() ;//so23423432809432084
    35. Cookie c = new Cookie("tokenId",tokenId) ;
    36. c.setMaxAge(60);//cookie在浏览器的存活时间 秒为单位
    37. resp.addCookie(c);
    38. Token token = new Token(
    39. tokenId,
    40. user,
    41. req.getRemoteAddr(), //可以获得局域网ip,广域网使用代理后还要其他方式获得
    42. System.currentTimeMillis(),
    43. System.currentTimeMillis()+1000L*60 // 七天:1000L*60*60*24*7
    44. );
    45. //token在服务端如何存储呢?
    46. //request不行,session也不行
    47. //数据库里,但有些浪费空间或影响效率
    48. //application(服务器运行期缓存), redis缓存中
    49. req.getServletContext().setAttribute(tokenId,token);
    50. }
    51. //登录成功,显示一个主界面 (间接响应)
    52. req.getSession().setAttribute("loginUser",user);
    53. resp.sendRedirect("main.html");
    54. //直接响应
    55. //resp.getWriter().write("main.html");
    56. }
    57. }
    58. }
    • 自动登录
      在首次登录时需要传递一个自动登录的标识
      在登录成功的前提下,处理自动登录
      方式一: 将账号密码随着cookie响应给浏览器
      浏览器下次请求时就会传递cookie中的账号密码
      就可以使用这个账号密码实现自动登录
      方式二: 将账号密码以及此次用户请求的一些信息存储在服务端
      用户本身的信息
      用户访问的ip信息(mac)
      用户浏览器信息
      并为之生成一个令牌
      将令牌码随着cookie响应给浏览器
      浏览器每次请求时都会传递cookie(令牌)
      每次请求前都需要验证一下
      是否需要自动登录
      * 是否已经登录 ```java //LoginFilter package com.duyi.qxgl.util;

    import com.duyi.qxgl.domain.User; import javax.servlet.*; import javax.servlet.http.HttpFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;

    // is — a public class LoginFilter extends HttpFilter implements Filter {

    // @Override // public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { @Override protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { //先除去不需要认证的 //登录不需要认证 Login.view , Login //.js, .jpg, *.css // request.getRequestURL(); // http://localhost:8080/qxgl/login.view // request.getRequestURI(); // /qxgl/login.view // request.getContextPath();// /qxgl 工程名 String path = request.getServletPath() ;//获得此次请求 login.view // System.out.println(“path:”+path); if(path.contains(“login”) || path.contains(“.js”) || path.contains(“.css”) || path.contains(“.png”)){ //不需要认证,放过此次请求,继续请求目标 chain.doFilter(request, response); return ; } //其余的请求再做登录认证 User user = (User) request.getSession().getAttribute(“loginUser”); if(user == null){ //需要认证,但发现还没登录 response.sendRedirect(“/qxgl”);//默认请求,则返回登录界面 }else { //需要认证,且认证通过,放过请求,继续访问目标 chain.doFilter(request,response); } } }

    1. ```java
    2. //AutoLoginFilter.java
    3. package com.duyi.qxgl.util;
    4. import com.duyi.qxgl.domain.Token;
    5. import javax.servlet.*;
    6. import javax.servlet.http.Cookie;
    7. import javax.servlet.http.HttpServletRequest;
    8. import javax.servlet.http.HttpServletResponse;
    9. import java.io.IOException;
    10. public class AutoLoginFilter implements Filter {
    11. @Override
    12. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    13. HttpServletRequest request = (HttpServletRequest) servletRequest;
    14. HttpServletResponse response = (HttpServletResponse) servletResponse;
    15. String path = request.getServletPath();
    16. if(path.contains("login") ||
    17. path.contains(".js") ||
    18. path.contains(".css") ||
    19. path.contains(".png")){
    20. //不需要认证,放过此次请求,继续请求目标
    21. filterChain.doFilter(request,response);
    22. return;
    23. }
    24. //无论是否自动登录,此次请求都正常访问
    25. // 获得所有的cookie
    26. Cookie[] cs = request.getCookies();
    27. if(cs != null && cs.length > 0){
    28. //有cookie
    29. for(Cookie c : cs){
    30. String name = c.getName();
    31. String value = c.getValue(); //tokenId的值=随机数
    32. if("tokenId".equals(name)){
    33. //value == uuid
    34. //有自动登录的处理
    35. Token token = (Token) request.getServletContext().getAttribute(value);
    36. if(token == null){
    37. //伪造的tokenId,不能自动登录,直接放过
    38. filterChain.doFilter(request,response);
    39. return;
    40. }else if(//判断ip是否一样
    41. token.getIp().equals(request.getRemoteAddr())
    42. ) {
    43. //截取的tokenId,不能自动登录
    44. filterChain.doFilter(request,response);
    45. return;
    46. }else if(//判断结束时间是否小于系统时间
    47. token.getEnd() < System.currentTimeMillis()
    48. ) {
    49. //token超时,不能自动登录
    50. filterChain.doFilter(request,response);
    51. return;
    52. }else {
    53. //符号登录条件
    54. request.getServletContext().setAttribute("LoginUser",token.getUser());
    55. return;
    56. }
    57. }
    58. }
    59. }
    60. filterChain.doFilter(request,response);
    61. }
    62. }
    1. //web.xml
    2. <?xml version="1.0" encoding="UTF-8"?>
    3. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    5. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    6. version="4.0">
    7. <welcome-file-list>
    8. <welcome-file>login.view</welcome-file>
    9. </welcome-file-list>
    10. <filter>
    11. <filter-name>LoginFilter</filter-name>
    12. <filter-class>com.duyi.qxgl.util.LoginFilter</filter-class>
    13. </filter>
    14. <filter-mapping>
    15. <filter-name>LoginFilter</filter-name>
    16. <!-- 发送所有请求都需要-->
    17. <url-pattern>/*</url-pattern>
    18. </filter-mapping>
    19. <filter>
    20. <filter-name>AutoLoginFilter</filter-name>
    21. <filter-class>com.duyi.qxgl.util.AutoLoginFilter</filter-class>
    22. </filter>
    23. <filter-mapping>
    24. <filter-name>AutoLoginFilter</filter-name>
    25. <url-pattern>/*</url-pattern>
    26. </filter-mapping>
    27. <!--
    28. 告诉tomvat,哪个请求,调用哪个Controller类(全名)
    29. 一旦根据请求匹配了controller类
    30. 就需要controller类符号Tomcat过程:实现Servle接口,继承HttpServlet父类
    31. * 所谓的符号规则,就是一定会有tomcat调用时所需的方法 service/doGet/doPost
    32. -->
    33. <servlet>
    34. <servlet-name>LoginController</servlet-name>
    35. <servlet-class>com.duyi.qxgl.controller.LoginController</servlet-class>
    36. </servlet>
    37. <servlet-mapping>
    38. <servlet-name>LoginController</servlet-name>
    39. <url-pattern>/login</url-pattern>
    40. </servlet-mapping>
    41. <servlet>
    42. <servlet-name>LoginView</servlet-name>
    43. <servlet-class>com.duyi.qxgl.view.LoginView</servlet-class>
    44. </servlet>
    45. <servlet-mapping>
    46. <servlet-name>LoginView</servlet-name>
    47. <url-pattern>/login.view</url-pattern>
    48. </servlet-mapping>
    49. <servlet>
    50. <servlet-name>FindUserController</servlet-name>
    51. <servlet-class>com.duyi.qxgl.controller.FindUserController</servlet-class>
    52. </servlet>
    53. <servlet-mapping>
    54. <servlet-name>FindUserController</servlet-name>
    55. <url-pattern>/findUser</url-pattern>
    56. </servlet-mapping>
    57. <servlet>
    58. <servlet-name>UserImportController</servlet-name>
    59. <servlet-class>com.duyi.qxgl.controller.UserImportController</servlet-class>
    60. </servlet>
    61. <servlet-mapping>
    62. <servlet-name>UserImportController</servlet-name>
    63. <url-pattern>/userImport</url-pattern>
    64. </servlet-mapping>
    65. </web-app>