手写Spring MVC框架(二) 实现访问拦截功能
前言
在上一篇文章中,我们手写了一个简单的mvc框架,今天我们要实现的功能点是:在Spring MVC框架基础上实现访问拦截功能。
先梳理一下需要实现的功能点:
- 搭建好Spring MVC基本框架;
- 定义注解@Security(有value属性,接收String数组),该注解用于添加在Controller类或者Handler方法上,表明哪些用户拥有访问该Handler方法的权限(注解配置用户名);
- 访问Handler时,用户名直接以参数名username紧跟在请求的url后面即可,比如http://localhost:8080/demo/testSecurity?username=zhangsan;
- 程序要进行验证,有访问权限则放行,没有访问权限在页面上输出。
实现过程
闲话少说,直接来看代码。
0、项目依赖
pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.hardy.edu</groupId>
<artifactId>springmvc-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging> <name>springmvc-demo Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties> <dependencies>
<!--引入spring webmvc的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.12.RELEASE</version>
</dependency> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency> <dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20140107</version>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build> </project>
1、注解开发
Security注解:
package com.hardy.edu.annotation; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; // 使用@Target注解,使该注解作用在方法上
@Target(ElementType.METHOD)
// 使用@Retention定义该注解在运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Security {
String[] value() default {};
}
2、拦截器开发
拦截器SecurityInterceptor:
package com.hardy.edu.interceptor; import com.hardy.edu.annotation.Security;
import org.json.JSONObject;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter; public class SecurityInterceptor implements HandlerInterceptor { /**
* 重写preHandle方法
* 该方法会在handler方法业务逻辑执行之前执行
* 往往在这里完成权限校验工作
* @param request
* @param response
* @param handler
* @return 返回值boolean代表是否放行,true代表放行,false代表中止
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("SecurityInterceptor preHandle......"); // 从url中获取username的值
String username = request.getParameter("username");
HandlerMethod method = (HandlerMethod) handler; // 获取testSecurity方法上的@Security注解
Security annotation = method.getMethod().getAnnotation(Security.class); // 获取Security注解中所标记的username列表,只有这些username有权限成功访问
String[] value = annotation.value(); // 判断url中输入的username值是否在Security注解中所标记的username列表中
boolean isHavePermissionName = false;
if(value != null){
for (int i = 0; i < value.length; i++) {
if(username.equals(value[i])){
isHavePermissionName = true;
break;
}
}
} // isHavePermissionName为false, 则没有权限访问
if(!isHavePermissionName){
JSONObject jsonObject = new JSONObject();
jsonObject.append("error", "没有访问权限");
System.out.println("该用户没有访问权限!");
// 设置响应编码类型
response.setCharacterEncoding("UTF-8");
// 设置相应内容类型
response.setContentType("application/json;charset=utf-8");
PrintWriter out = null; try{
// 向浏览器输出error信息
out = response.getWriter();
out.append(jsonObject.toString());
}catch(IOException e){
e.printStackTrace();
}finally {
if(out!=null){
out.close();
}
}
}
return true;
} /**
* 会在handler方法业务逻辑执行之后尚未跳转页面时执行
* @param request
* @param response
* @param handler
* @param modelAndView 封装了视图和数据,此时尚未跳转页面呢,你可以在这里针对返回的数据和视图信息进行修改
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("SecurityInterceptor postHandle......");
} /**
* 页面已经跳转渲染完毕之后执行
* @param request
* @param response
* @param handler
* @param ex 可以在这里捕获异常
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("SecurityInterceptor afterCompletion......");
} }
3、自定义类型转换器
日期转换器DateConverter:
package com.hardy.edu.converter; import org.springframework.core.convert.converter.Converter; import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date; /**
* @Author: HardyYao
* @Date: 2021/5/11
* 自定义类型转换器
* S:source,源类型
* T:target:目标类型
*/
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
// 完成字符串向日期的转换
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); try {
Date parse = simpleDateFormat.parse(source);
return parse;
} catch (ParseException e) {
e.printStackTrace();
} return null;
}
}
4、编写控制器
控制器DemoController:
package com.hardy.edu.controller; import com.hardy.edu.annotation.Security;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.text.SimpleDateFormat;
import java.util.Date; /**
* @Author: HardyYao
* @Date: 2021/5/11
*/
@Controller
@RequestMapping("/demo")
public class DemoController { @Security(value = {"hardy","zhangsan","lisi"})
@RequestMapping("/testSecurity")
public ModelAndView testSecurity(HttpServletRequest request, HttpServletResponse response,HttpSession session) {
String username = request.getParameter("username");
ModelAndView modelAndView = new ModelAndView();
Date date = new Date();
// 实现日期向字符串的转换
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
modelAndView.addObject("date", simpleDateFormat.format(date));
modelAndView.addObject("username",username); modelAndView.setViewName("success");
return modelAndView;
} }
5、编写配置文件
web.xml:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app>
<display-name>Archetype Created Web Application</display-name> <!--springmvc提供的针对post请求的编码过滤器-->
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter> <!--配置springmvc请求方式转换过滤器,会检查请求参数中是否有_method参数,如果有就按照指定的请求方式进行转换-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter> <filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> <filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> <servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name> <!--
方式一:带后缀,比如*.action *.do *.aaa
该种方式比较精确、方便,在以前和现在企业中都有很大的使用比例
方式二:/ 不会拦截 .jsp,但是会拦截.html等静态资源(静态资源:除了servlet和jsp之外的js、css、png等) 为什么配置为/ 会拦截静态资源???
因为tomcat容器中有一个web.xml(父),你的项目中也有一个web.xml(子),是一个继承关系
父web.xml中有一个DefaultServlet, url-pattern 是一个 /
此时我们自己的web.xml中也配置了一个 / ,覆写了父web.xml的配置
为什么不拦截.jsp呢?
因为父web.xml中有一个JspServlet,这个servlet拦截.jsp文件,而我们并没有覆写这个配置,
所以springmvc此时不拦截jsp,jsp的处理交给了tomcat 如何解决/拦截静态资源这件事? 方式三:/* 拦截所有,包括.jsp
-->
<!--拦截匹配规则的url请求,进入springmvc框架处理-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
springmvc.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
"> <!--开启controller扫描-->
<context:component-scan base-package="com.hardy.edu.controller"/> <!--配置springmvc的视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean> <!--
自动注册最合适的处理器映射器,处理器适配器(调用handler方法)
-->
<mvc:annotation-driven conversion-service="conversionServiceBean"/> <!--注册自定义类型转换器-->
<bean id="conversionServiceBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.hardy.edu.converter.DateConverter"></bean>
</set>
</property>
</bean> <!--静态资源配置,方案一-->
<!--
原理:添加该标签配置之后,会在SpringMVC上下文中定义一个DefaultServletHttpRequestHandler对象
这个对象如同一个检查人员,对进入DispatcherServlet的url请求进行过滤筛查,如果发现是一个静态资源请求
那么会把请求转由web应用服务器(tomcat)默认的DefaultServlet来处理,如果不是静态资源请求,那么继续由
SpringMVC框架处理
-->
<!--<mvc:default-servlet-handler/>--> <!--静态资源配置,方案二,SpringMVC框架自己处理静态资源
mapping:约定的静态资源的url规则
location:指定的静态资源的存放位置 -->
<mvc:resources location="classpath:/" mapping="/resources/**"/> <mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.hardy.edu.interceptor.SecurityInterceptor"></bean>
</mvc:interceptor> </mvc:interceptors> </beans>
6、编写jsp页面
error.jsp:
<%@ page language="java" isELIgnored="false" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Insert title here</title>
</head>
<body>
异常信息: ${msg}
</body>
</html>
success.jsp:
<%@ page language="java" isELIgnored="false" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Insert title here</title>
</head>
<body>
${username}: 跳转成功!服务器时间:${date}
</body>
</html>
项目整体结构
项目运行结果
启动项目后输入地址进行访问,可以看到控制台输出以下信息:
访问:http://localhost:8080/demo/testSecurity?username=hardy
因为hardy在授权列表中,故可以访问成功。
下面访问:http://localhost:8080/demo/testSecurity?username=wangwu
因为wangwu不在授权列表中,故访问失败。
总结
今天我们在Spring MVC框架基础上实现了访问拦截功能,这里的核心代码是Security注解及Security拦截器,功能也比较简单,但是这里的原理与常见的登录拦截功能是相通的,有兴趣的朋友可以在此基础上实现一个真正的登录拦截功能。
手写Spring MVC框架(二) 实现访问拦截功能的更多相关文章
- 手写Spring MVC框架(一) 实现简易版mvc框架
前言 前面几篇文章中,我们讲解了Spring MVC执⾏的⼤致原理及关键组件的源码解析,今天,我们来模仿它⼿写⾃⼰的mvc框架. 先梳理一下需要实现的功能点: tomcat加载配置文件web.xml: ...
- 【Spring】手写Spring MVC
Spring MVC原理 Spring的MVC框架主要由DispatcherServlet.处理器映射.处理器(控制器).视图解析器.视图组成. 完整的Spring MVC处理 流程如下: Sprin ...
- 手写Spring MVC
闲及无聊 又打开了CSDN开始看一看有什么先进的可以学习的相关帖子,这时看到了一位大神写的简历装X必备,手写Spring MVC. 我想这个东西还是有一点意思的 就拜读了一下大佬的博客 通读了一遍相关 ...
- 手写Spring事务框架
Spring事务基于AOP环绕通知和异常通知 编程事务 声明事务 Spring事务底层使用编程事务+AOP进行包装的 = 声明事务 AOP应用场景: 事务 权限 参数验证 什么是AOP技术 AO ...
- 手写spring事务框架-蚂蚁课堂
1.视频参加C:\Users\Administrator\Desktop\蚂蚁3期\[www.zxit8.com] 0017-(每特教育&每特学院&蚂蚁课堂)-3期-源码分析-手写Sp ...
- 一个老程序员是如何手写Spring MVC的
人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十 ...
- 手写spring事务框架, 揭秘AOP实现原理。
AOP面向切面编程:主要是通过切面类来提高代码的复用,降低业务代码的耦合性,从而提高开发效率.主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等. AOP实现原理:aop是通过cgli ...
- 从零开始手写 spring ioc 框架,深入学习 spring 源码
IoC Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理. 创作目的 使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过 ...
- 手写 Spring
手写 Spring 不多说,简历装 X 必备.不过练好还是需要求一定的思维能力. 一.整体思路 思路要熟练背下来 1)配置阶段 配置 web.xml: XDispatchServlet 设定 init ...
随机推荐
- error: failed to push some refs to 'XXX'
遇到上述无法提交的问题:都是这种命令git push -u origin master造成的 查看github上的提示: 解决:把之前的命令中的master修改成main就好了
- C++并发与多线程学习笔记--基本概念和实现
基本概念 并发 可执行程序.进程.线程 学习心得 并发的实现方法 多进程并发 多线程并发 总结 C++标准库 基本概念 (并发.进程.线程)区分C++初级编程和中高级编程 并发 两个或者更多的任务同时 ...
- 在PHP7以上版本使用不了mysql扩展
旧程序使用了mysql扩展,而新环境却是PHP7以上版本,不支持mysql扩展,办法是将旧程序中的mysql相关内容修改为mysqli或PDO代码. 但是涉及修改的量大,那则可以包含(include ...
- 关于C语言解决汉诺塔(hanoi)问题
C语言解决汉诺塔问题 汉诺塔是典型的递归调用问题: hanoi简介:印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔.不论白天黑夜,总有一个僧侣 ...
- day-8 xctf-guess_num
xctf-guess_num 题目传送门:https://adworld.xctf.org.cn/task/answer?type=pwn&number=2&grade=0&i ...
- JVM经典垃圾收集器
这个关系不是一成不变的,由于维护和兼容性测试的成本,在JDK 8时将Serial+CMS. ParNew+Serial Old这两个组合声明为废弃(JEP 173),并在JDK 9中完全取消了这些 ...
- 日志功能 - 使用 conf 配置文件
配置文件:Logger.conf 工具类:LogUtil.py 测试类:testDemo.py 执行效果 配置文件:Logger.conf 定义日志的类型.级别.格式等信息. [loggers] # ...
- 计算机网络第一章bb测试
错题8,31 课程 211计算机网络 测试 网络概论与体系结构 状态 已完成 尝试分数 得 340 分,满分 360 分 已用时间 14 分钟 说明 第一章 网络概论测试 显示的结果 所有答案, 已提 ...
- 关于SpringBoot结合mybatis后遇到的坑
先放出我遇到的出错信息,真的出错了可以先看看出错信息,就能更加高效准确的搜索到信息 我的报错日志: org.springframework.beans.factory.UnsatisfiedDepen ...
- 【译】Android API 规范
[译]Android API 规范 译者按: 修改R代码遇到Lint tool的报错,搜到了这篇文档,aosp仓库地址:Android API Guidelines. 58e9b5f Project ...