一、Filter 过滤器概述
1、Filter 介绍
(1)介绍
- Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源如Jsp、Servlet、静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
- 通常使用过滤器Filter来做登录验证、会话管理、日志记录以及一些通用的操作,过滤器需要实现Filter接口
- Filter过滤器的执行顺序是在Servlet之前的,其生命周期和Servlet类似
创建 => 初始化 => 过滤 => 销毁
(2)Filter 的作用
- 在客户端的请求访问后端资源之前,拦截这些请求。
- 在服务器的响应发送回客户端之前,处理这些响应。
2、常见过滤器
- 身份验证过滤器(Authentication Filters)。
- 数据压缩过滤器(Data compression Filters)。
- 加密过滤器(Encryption Filters)。
- 触发资源访问事件过滤器。
- 图像转换过滤器(Image Conversion Filters)。
- 日志记录和审核过滤器(Logging and Auditing Filters)。
- MIME-TYPE 链过滤器(MIME-TYPE Chain Filters)。
- 标记化过滤器(Tokenizing Filters)。
- XSL/T 过滤器(XSL/T Filters),转换 XML 内容。
3、Filter 配置文件
<filter>
<filter-name></filter-name>
<filter-class></filter-class>
</filter->
<filter-mapping>
<filter-name></filter-name>
<url-pattern><filter-pattern>
</filter-mapping>
- filter-name:过滤器类的名称,如
MyFilter
- filter-class:过滤器类的路径,如
com.java.filter.MyFilter
- url-pattern:拦截过滤规则,如
/*
:表示拦截所有*.do
:表示以.do
结尾的都会被拦截进入过滤器,一般用于某个模块的拦截处理/test.do
:表示拦截一个指定url的请求,一般用于比较重要的servlet针对性的拦截
4、Filter 执行过程
- 客户端发起请求到服务器;
- 服务器接收到请求后,根据URI信息在web.xml中找到对应的过滤器执行doFilter方法,该方法对此次请求进行处理后如果符合要求则放行;
- 放行后如果还有符合要求的过滤则继续进行过滤,找到执行对应的servlet进行请求处理;
- servlet对请求处理完毕后,也就service方法结束后,还需继续返回相应的doFilter方法继续执行。
二、Filter过滤器示例
1、过滤器完成多点登陆的问题
(1)使用session完成会话跟踪
- 在用户没有登陆的情况下,不允许访问一些网页和资源
- 用户登陆成功以后,将用户信息保存在session对象中
- 通过登陆过滤器判断用户是否已经登陆,如果登陆放行,否则就冲定向到登陆网页
- 不需要登陆也能访问的资源
(2)使用Cookie记录登陆用户状态实现自动登陆
- 在用户登陆成功以后,生成一个不可重复的随机字符串作为用户的登陆令牌信息
- 将该令牌信息保存到数据库该用户的数据中
- 使用Cookie将该令牌信息记录到浏览器
- 登陆过滤器中获取(token)Cookie,通过token从数据库中获取用户数据,再将用户数据保
(3)解决多点登陆的问题
2、代码
(1)model 层
① users 类的失血模型
public class Users {
private int userId;
private String userAccount;
private String userPassWord;
private String userTaken;
//省略getter、setter
}
(2)util 层
① JDBCUtil 类:连接数据库
public class JDBCUtil {
public static final ThreadLocal<Connection> threadLocal=new ThreadLocal<Connection>();
private static String driver = null;
private static String url = null;
private static String user = null;
private static String password = null;
static Scanner sc = new Scanner(System.in);
//静态代码块
static{
try {
//获取文件流
InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("user.properties");
//创建properties对象
Properties prop = new Properties();
//使用prop对象的load()方法加载流
prop.load(is);
//获取其文件的数据
driver = prop.getProperty("river");
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password");
//注册驱动
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection registJDBC() throws Exception {
return DriverManager.getConnection(url, user, password);
}
//关流
public static void close(ResultSet resultSet, PreparedStatement ps, Connection conn) {
if(resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(ps != null) {
try {
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
② FinalDates 类:静态常量
public class FinalDates {
public static final String USERS = "users";
public static final String TAKEN="user_taken";
}
(3)service 层
① UserService 类:UserService 的接口类
public interface UserService {
//定义Service层查询用户账户的方法
public Users readAccount(String user_Account);
//定义Service层修改用户登录令牌的方法
public void updateTaken(int user_Id, String user_Taken);
//定义Service层查询用户登录令牌的方法
public Users readTaken(String user_Taken);
}
② Imp_UserService 类:实现 UserService 接口
public class Imp_UserService implements UserService{
private UserDao userDao = new Imp_UserDao();
@Override
public Users readAccount(String user_Account) {
Users users = null;
try {
users = userDao.readAccount(user_Account);
}catch(Exception e) {
e.printStackTrace();
}
return users;
}
@Override
public void updateTaken(int user_Id, String user_Taken) {
Connection conn=null;
try {
conn = JDBCUtil.registJDBC();
//关闭自动提交
conn.setAutoCommit(false);
JDBCUtil.threadLocal.set(conn);
userDao.updateTaken(user_Id, user_Taken);
//提交事务
conn.commit();
} catch (Exception e) {
try {
//回滚事务
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
@Override
public Users readTaken(String user_Taken) {
Users users = null;
try {
users = userDao.readTaken(user_Taken);
}catch(Exception e) {
e.printStackTrace();
}
return users;
}
}
(4)dao 层
① UserDao 类:UserDao 的接口类
public interface UserDao {
//定义Dao层一个查询用户账户的方法
public Users readAccount(String user_Account) throws Exception;
//定义Dao层一个修改用户登录令牌的方法
public void updateTaken(int user_Id,String user_Taken) throws Exception;
//定义Dao层一个查询用户登录令牌的方法
public Users readTaken(String user_Taken) throws Exception;
}
② Imp_UserDao 类:实现 UserDao 接口
public class Imp_UserDao implements UserDao{
@Override
public Users readAccount(String user_Account) throws Exception {
Connection conn = JDBCUtil.registJDBC();
String sql = "SELECT user_id,user_account,user_password,user_taken FROM users WHERE user_account = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, user_Account);
ResultSet resultSet = ps.executeQuery();
Users users = null;
if(resultSet.next()) {
users = new Users();
users.setUserId(resultSet.getInt("user_id"));
users.setUserAccount(resultSet.getString("user_account"));
users.setUserPassWord(resultSet.getString("user_password"));
users.setUserTaken(resultSet.getString("user_taken"));
}
JDBCUtil.close(resultSet, ps, conn);
return users;
}
@Override
public void updateTaken(int user_Id, String user_Taken) throws Exception {
Connection conn = JDBCUtil.threadLocal.get();
String sql = "UPDATE users SET user_taken = ? WHERE user_id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, user_Taken);
ps.setInt(2, user_Id);
ps.executeUpdate();
ps.close();
}
@Override
public Users readTaken(String user_Taken) throws Exception {
Connection conn = JDBCUtil.registJDBC();
String sql = "SELECT user_id, user_account, user_password, user_taken FROM users WHERE user_taken = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, user_Taken);
ResultSet resultSet = ps.executeQuery();
Users users = null;
if(resultSet.next()) {
users=new Users();
users.setUserId(resultSet.getInt("user_id"));
users.setUserAccount(resultSet.getString("user_account"));
users.setUserPassWord(resultSet.getString("user_password"));
users.setUserTaken(resultSet.getString("user_taken"));
}
JDBCUtil.close(resultSet, ps, conn);
return users;
}
}
(5)filter 层
① LoginFilter 类:登录的过滤器操作
创建的过滤器类需要实现Filter接口,并且重写doFilter方法
@WebFilter("/*")
public class LoginFilter implements Filter{
private List<String> starts = new ArrayList<String>();
private List<String> ends = new ArrayList<String>();
@Override
public void init(FilterConfig config) throws ServletException {
//存入后缀需要放行的地址
ends.add("css");
ends.add("js");
ends.add("jpg");
ends.add("png");
ends.add("gif");
//存入前缀需要放行的地址
starts.add("login");
starts.add("loginservlet");
starts.add("LoginServlet");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,FilterChain chain) throws IOException, ServletException {
//设置编码格式
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//获取本次请求的请求路径
HttpServletRequest request = (HttpServletRequest)req;
String uri = request.getRequestURI().toLowerCase();
//提取出前缀和后缀
String resourceURI = uri.substring(uri.lastIndexOf("/") + 1 );
String start = null;
String end = null;
int index = resourceURI.lastIndexOf(".");
if(index != -1) {
start = resourceURI.substring(0, index);
end = resourceURI.substring(index + 1);
}else {
start = resourceURI;
}
//判断
if(starts.contains(start) || ends.contains(end)) {
System.out.println("进来0");
//满足条件放行
chain.doFilter(req, resp);
}else {
//判断浏览器发送的请求中的cookie信息是否包含taken
Cookie[] cookies = request.getCookies();
String taken = null;
if(cookies != null) {
for(Cookie cookie: cookies) {
if(cookie.getName().equals(FinalDates.TAKEN)) {
taken = cookie.getValue();
}
}
}
if(taken == null) {
HttpServletResponse response=(HttpServletResponse)resp;
// chain.doFilter(req, resp);
response.sendRedirect("/GouYaoDemo/jsp/login.jsp");
}else {
UserService userService = new Imp_UserService();
Users users = userService.readTaken(taken);
if(users != null) {
HttpSession session = request.getSession();
session.setAttribute(FinalDates.USERS, users);
chain.doFilter(req, resp);
}else {
String message = "您的账号在别处登录";
request.setAttribute("message", message);
request.getRequestDispatcher("/jsp/login.jsp").forward(request, resp);
}
}
}
}
@Override
public void destroy() {
}
}
- init():初始化,在这里可以配置允许直接放行的文件类型等,在服务器启动就会执行。
- doFilter():这个时拦截请求的方法,在这个方法里,了可以对资源进行管理和分配,
chain.doFilter()
表示手动对满足条件的请求进行放行。 - destroy():销毁,在服务器关闭的时候执行。
(6)servlet 层
① LoginServlet 类:用户登录操作类(包括用户登录信息判断等)
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet{
@Override
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//处理乱码
request.setCharacterEncoding("utf-8");
//获取输入框的数据
String userName = request.getParameter("username");
String passWord = request.getParameter("password");
//定义一个字符串用于给用户反馈登录情况信息
String message = null;
//防止用户直接输入地址访问,在后端设定用户名和密码为空的判断
//判断用户名是否为空
if(userName == null || "".equals(userName)) {
message = "用户名不能为空";
request.setAttribute("message", message);
//用户名为空的情况下再次请求转发到登录界面
request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
return;
}
//判断密码是否为空
if(passWord == null || "".equals(passWord)) {
message = "密码不能为空";
request.setAttribute("message", message);
//密码为空的情况下再次请求转发到登录界面
request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
return;
}
//判断用户账号是否存在以及账号密码是否正确
UserService userService = new Imp_UserService();
//通过service层调用dao层的查询用于账户的方法,并返回一个Users对象
Users users = userService.readAccount(userName);
if(users == null) {
message = "用户名不存在";
request.setAttribute("message", message);
request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
}else {
if(!passWord.equals(users.getUserPassWord())) {
message = "密码错误";
request.setAttribute("message", message);
request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
}else {
/*
* 用户账号密码都正确,将用户对象保存到Session中,
* 并随机生成一个用户登录令牌存入到数据库和浏览器中,
* 最后跳转到另一个页面,
*/
request.getSession().setAttribute(FinalDates.USERS, users);
String userTaken = UUID.randomUUID().toString().replace("-", "");
//将登录令牌更新到数据库中的用户数据
userService.updateTaken(users.getUserId(), userTaken);
//将登录令牌保存到浏览器中
Cookie cookie = new Cookie(FinalDates.TAKEN, userTaken);
cookie.setMaxAge(60*60*7);
//将Cookie响应到浏览器
response.addCookie(cookie);
//请求转发到另一个网页
request.getRequestDispatcher("/jsp/main.jsp").forward(request, response);
}
}
}
}