前言

在上一篇文章中,我们手写了一个简单的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框架(二) 实现访问拦截功能的更多相关文章

  1. 手写Spring MVC框架(一) 实现简易版mvc框架

    前言 前面几篇文章中,我们讲解了Spring MVC执⾏的⼤致原理及关键组件的源码解析,今天,我们来模仿它⼿写⾃⼰的mvc框架. 先梳理一下需要实现的功能点: tomcat加载配置文件web.xml: ...

  2. 【Spring】手写Spring MVC

    Spring MVC原理 Spring的MVC框架主要由DispatcherServlet.处理器映射.处理器(控制器).视图解析器.视图组成. 完整的Spring MVC处理 流程如下: Sprin ...

  3. 手写Spring MVC

    闲及无聊 又打开了CSDN开始看一看有什么先进的可以学习的相关帖子,这时看到了一位大神写的简历装X必备,手写Spring MVC. 我想这个东西还是有一点意思的 就拜读了一下大佬的博客 通读了一遍相关 ...

  4. 手写Spring事务框架

    Spring事务基于AOP环绕通知和异常通知 编程事务 声明事务 Spring事务底层使用编程事务+AOP进行包装的   = 声明事务 AOP应用场景:  事务 权限 参数验证 什么是AOP技术 AO ...

  5. 手写spring事务框架-蚂蚁课堂

    1.视频参加C:\Users\Administrator\Desktop\蚂蚁3期\[www.zxit8.com] 0017-(每特教育&每特学院&蚂蚁课堂)-3期-源码分析-手写Sp ...

  6. 一个老程序员是如何手写Spring MVC的

    人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十 ...

  7. 手写spring事务框架, 揭秘AOP实现原理。

    AOP面向切面编程:主要是通过切面类来提高代码的复用,降低业务代码的耦合性,从而提高开发效率.主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等. AOP实现原理:aop是通过cgli ...

  8. 从零开始手写 spring ioc 框架,深入学习 spring 源码

    IoC Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理. 创作目的 使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过 ...

  9. 手写 Spring

    手写 Spring 不多说,简历装 X 必备.不过练好还是需要求一定的思维能力. 一.整体思路 思路要熟练背下来 1)配置阶段 配置 web.xml: XDispatchServlet 设定 init ...

随机推荐

  1. P1048_采药(JAVA语言)

    思路:动态规划的背包问题.把时间看作重量,转换为01背包问题求解. 题目描述 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师.为此,他想拜附近最有威望的医师为师.医师为了判断他的资质,给他出 ...

  2. 9、Spring教程之AOP

    那我们接下来就来聊聊AOP吧! 1.什么是AOP AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.A ...

  3. 前端vue性能优化

    一:代码层次优化 1.1.v-if 和 v-show 区分使用场景 v-if 是 真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建:也是惰性的:如果在初始渲染时 ...

  4. Java进阶专题(二十八) Service Mesh初体验

    前言 ​ ⽬前,微服务的架构⽅式在企业中得到了极⼤的发展,主要原因是其解决了传统的单体架构中存在的问题.当单体架构拆分成微服务架构就可以⾼枕⽆忧了吗? 显然不是的.微服务架构体系中同样也存在很多的挑战 ...

  5. SpringCloud(六)分布式事务

    在分布式系统中,分布式事务基本上是绕不开的, 分布式事务是指事务的参与者.支持事务的服务器.资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上 .其实就可以简单理解成在分布式系统中实现事务 ...

  6. 面试准备——计算机网络(http)

    一.各种协议与HTTP协议之间的关系 二.URI(统一资源标识符) URI用字符串标识某一互联网资源. URI的格式: 协议方案名:指定访问资源时使用的协议类型. 登录信息(认证):可选,指定用户名和 ...

  7. 【洛谷】P1294 高手去散步

    题目背景 高手最近谈恋爱了.不过是单相思."即使是单相思,也是完整的爱情",高手从未放弃对它的追求.今天,这个阳光明媚的早晨,太阳从西边缓缓升起.于是它找到高手,希望在晨读开始之前 ...

  8. Qt开发技术:图形视图框架(一)基本介绍

    前话   使用到Qt的视图框架.   Qt视图框架介绍 简介   图形视图框架(The Graphic View Framework)用于管理和与大量定制的二维图形项目交互,以及用于可视化项目的视图小 ...

  9. Tree Recovery UVA - 536

    Little Valentine liked playing with binary trees very much. Her favorite game was constructing rando ...

  10. struct 模块

    1. Struct 简介 2. Struct 代码示例 2.1 struct.pack 2.2 struct.unpack 2.3 struct.calcsize 1. Struct 简介 当 pyt ...