- 登录认证功能:
从业务流程而言,只有登录后才能访问其他的功能/页面
从技术而言,访问的所有内容都是通过url请求向服务器获取的
从技术而言,在不登录的情况,只要获得url,也可以向服务器访问正常登录才能访问的内容
明显不合理
就在每次访问非登录的功能时,都判断一下是否已经登录了。
此登录认证功能具有以下特点
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() {
return tokenid;
} public void setTokenid(String tokenid) {
this.tokenid = tokenid;
} public User getUser() {
return user;
} public void setUser(User user) {
this.user = user;
} public String getIp() {
return ip;
} public void setIp(String ip) {
this.ip = ip;
} public long getStart() {
return start;
} public void setStart(long start) {
this.start = start;
} public long getEnd() {
return end;
} public void setEnd(long end) {
this.end = end;
} public Token(String tokenid, User user, String ip, long start, long end) {
this.tokenid = tokenid;this.user = user;this.ip = ip;this.start = start;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(“
/*servlet 和 jsp都可以拼装动态网页servlet = java(主场) + htmljsp = java + html(主场)*/}
}
```java//LoginController.javapackage com.duyi.qxgl07.controller;import com.duyi.qxgl07.domain.Token;import com.duyi.qxgl07.domain.User;import com.duyi.qxgl07.service.UserService;import javax.servlet.ServletException;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.UUID;public class LoginController extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//验证此次登录的账号和密码是否正确//要想验证,需要先获得账号密码//如何获得账号密码//根据请求时传递的参数key,获得对应的参数值。 这个key就是网页中input.name值,获得的参数值就是网页中input输入的值String uname = req.getParameter("uname") ;String upass = req.getParameter("upass") ;String autoflag = req.getParameter("autoflag");//service查找UserService service = new UserService();User user = service.checkLogin(uname,upass);if(user == null){//根据账号密码,没有找到数据,登录失败,重新显示登录页面resp.sendRedirect("login.view?f=9");}else{//自动登录处理if(autoflag != null && !"".equals(autoflag)){//要实现自动登录String tokenId = UUID.randomUUID().toString() ;//so23423432809432084Cookie c = new Cookie("tokenId",tokenId) ;c.setMaxAge(60);//cookie在浏览器的存活时间 秒为单位resp.addCookie(c);Token token = new Token(tokenId,user,req.getRemoteAddr(), //可以获得局域网ip,广域网使用代理后还要其他方式获得System.currentTimeMillis(),System.currentTimeMillis()+1000L*60 // 七天:1000L*60*60*24*7);//token在服务端如何存储呢?//request不行,session也不行//数据库里,但有些浪费空间或影响效率//application(服务器运行期缓存), redis缓存中req.getServletContext().setAttribute(tokenId,token);}//登录成功,显示一个主界面 (间接响应)req.getSession().setAttribute("loginUser",user);resp.sendRedirect("main.html");//直接响应//resp.getWriter().write("main.html");}}}
- 自动登录
在首次登录时需要传递一个自动登录的标识
在登录成功的前提下,处理自动登录
方式一: 将账号密码随着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); } } }
```java//AutoLoginFilter.javapackage com.duyi.qxgl.util;import com.duyi.qxgl.domain.Token;import javax.servlet.*;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class AutoLoginFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;String path = request.getServletPath();if(path.contains("login") ||path.contains(".js") ||path.contains(".css") ||path.contains(".png")){//不需要认证,放过此次请求,继续请求目标filterChain.doFilter(request,response);return;}//无论是否自动登录,此次请求都正常访问// 获得所有的cookieCookie[] cs = request.getCookies();if(cs != null && cs.length > 0){//有cookiefor(Cookie c : cs){String name = c.getName();String value = c.getValue(); //tokenId的值=随机数if("tokenId".equals(name)){//value == uuid//有自动登录的处理Token token = (Token) request.getServletContext().getAttribute(value);if(token == null){//伪造的tokenId,不能自动登录,直接放过filterChain.doFilter(request,response);return;}else if(//判断ip是否一样token.getIp().equals(request.getRemoteAddr())) {//截取的tokenId,不能自动登录filterChain.doFilter(request,response);return;}else if(//判断结束时间是否小于系统时间token.getEnd() < System.currentTimeMillis()) {//token超时,不能自动登录filterChain.doFilter(request,response);return;}else {//符号登录条件request.getServletContext().setAttribute("LoginUser",token.getUser());return;}}}}filterChain.doFilter(request,response);}}
//web.xml<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><welcome-file-list><welcome-file>login.view</welcome-file></welcome-file-list><filter><filter-name>LoginFilter</filter-name><filter-class>com.duyi.qxgl.util.LoginFilter</filter-class></filter><filter-mapping><filter-name>LoginFilter</filter-name><!-- 发送所有请求都需要--><url-pattern>/*</url-pattern></filter-mapping><filter><filter-name>AutoLoginFilter</filter-name><filter-class>com.duyi.qxgl.util.AutoLoginFilter</filter-class></filter><filter-mapping><filter-name>AutoLoginFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!--告诉tomvat,哪个请求,调用哪个Controller类(全名)一旦根据请求匹配了controller类就需要controller类符号Tomcat过程:实现Servle接口,继承HttpServlet父类* 所谓的符号规则,就是一定会有tomcat调用时所需的方法 service/doGet/doPost--><servlet><servlet-name>LoginController</servlet-name><servlet-class>com.duyi.qxgl.controller.LoginController</servlet-class></servlet><servlet-mapping><servlet-name>LoginController</servlet-name><url-pattern>/login</url-pattern></servlet-mapping><servlet><servlet-name>LoginView</servlet-name><servlet-class>com.duyi.qxgl.view.LoginView</servlet-class></servlet><servlet-mapping><servlet-name>LoginView</servlet-name><url-pattern>/login.view</url-pattern></servlet-mapping><servlet><servlet-name>FindUserController</servlet-name><servlet-class>com.duyi.qxgl.controller.FindUserController</servlet-class></servlet><servlet-mapping><servlet-name>FindUserController</servlet-name><url-pattern>/findUser</url-pattern></servlet-mapping><servlet><servlet-name>UserImportController</servlet-name><servlet-class>com.duyi.qxgl.controller.UserImportController</servlet-class></servlet><servlet-mapping><servlet-name>UserImportController</servlet-name><url-pattern>/userImport</url-pattern></servlet-mapping></web-app>
