基于SpringMVC实现常见功能

本文详细介绍了如何在基于SpringMVC的项目中防止XSS攻击,包括使用@InitBinder注解进行全局数据过滤,以及如何配置ApacheShiro进行身份验证、授权和缓存管理。同时涵盖了Shiro的Realm定制和使用EhCache作为缓存机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于SpringMVC实现常见功能

防止XSS攻击

XSS攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。

在后端基于SpringMVC框架过滤XSS攻击还是比较简单,只需要使用@InitBinder注解即可。

导入Jar包
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.2</version>
</dependency>
过滤XSS攻击
import java.beans.PropertyEditorSupport;

import org.apache.commons.text.StringEscapeUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;

public abstract class BaseController {
	@InitBinder
	protected void initBinder(WebDataBinder binder){
		// String类型转换,将所有传递进来的String进行HTML编码,防止XSS攻击
		binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				setValue(text == null ? 
						null :  StringEscapeUtils.escapeHtml4(text.trim()));
			}
			@Override
			public String getAsText() {
				Object value = getValue();
				return value == null ? "" : value.toString();
			}
		});
	}
}
继承BaseController即可

通过上述的步骤即可过滤XSS攻击。

数据转换

很多时候,前端数据传过来,后端并不能正常接收,比如日期的格式外国人和国人习惯就是不一样的,需要转换格式,还有比如XSS攻击,后端也需要进行数据转换。

上面使用@InitBinder添加自定义编辑器转换数据,这次主要讲如何使用WebBindingInitializer注册全局自定义编辑器转换数据。

程序
import java.beans.PropertyEditorSupport;

import org.apache.commons.text.StringEscapeUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebBindingInitializer;
import org.springframework.web.context.request.WebRequest;


public class TextBindingInitializer 
			implements WebBindingInitializer {

	@Override
	public void initBinder(WebDataBinder binder, WebRequest request) {
		binder.registerCustomEditor(String.class, 
				new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				setValue(text == null ? null :  StringEscapeUtils.escapeHtml4(text.trim()));
			}
			@Override
			public String getAsText() {
				Object value = getValue();
				return value == null ? "" : value.toString();
			}
		});
	}
}
配置
<bean class="
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="webBindingInitializer">
        <bean class="com.plf.base.TextBindingInitializer" />
    </property>
</bean>

注意

已经废弃
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

建议使用
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

当需要使用全局处理的时候可以使用WebBindingInitializer,如果只是局部处理那就可以使用@InitBinder

静态资源的配置

SpringMVC配置前端控制器的时候,一般建议配置为*.do这种方式,这是不会存在访问不到静态资源的问题,但是目前比较流行RESTful风格,就需要配置 \ ,这样就需要配置静态资源的访问路径,不然就访问不到。

第一种
在springmvc中配置:

第一步需要 http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd 约束

第二步配置 <mvc:default-servlet-handler />即可

<mvc:default-servlet-handler />会将对静态资源的访问请求通过HandlerMapping映射到默认
Servlet请求处理器DefaultServletHttpRequestHandler对象。
而处理器调用了Tomcat的DefaultServlet来处理静态资源的访问请求。

其实就是将SpringMVC不能处理的请求交给tomcat。
第二种
<servlet-mapping>
  	<servlet-name>default</servlet-name>
  	<url-pattern>*.jpg</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
  	<servlet-name>default</servlet-name>
  	<url-pattern>*.js</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
  	<servlet-name>default</servlet-name>
  	<url-pattern>*.css</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
  	<servlet-name>default</servlet-name>
  	<url-pattern>*.png</url-pattern>
  </servlet-mapping>
第三种
在springmvc.xml中配置
<mvc:resources location="/static/" mapping="/static/**"/>
	
在spring3.0.4版本之后,Spring中定义了专门用于处理静态资源访问请求的处理器
ResourceHttpRequestHandler,并且添加了<mvc:resources />标签,专门用于解决
静态资源无法访问问题。

location 表示静态资源所在目录。目录可以是WEB-INF/目录以及子目录
mapping 表示对该以资源的请求

该配置会把静态资源的访问请求经HandlerMapping直接映射到
静态资源处理器对象ResourceHttpRequestHandler

集成Shiro的配置

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、加密和会话管理。所以它用途广泛,在大部分系统中都有应用,本博客主要讲解基于Spring的配置方法。

Shiro小巧而且简单,使用方便,很适合高效编码。

学习Shiro的基本使用可以参考:http://wiki.jikexueyuan.com/project/shiro/

github上也有相关资料参考:https://github.com/zhangkaitao/shiro-example

Shiro核心组件
  • Subject:表示当前的“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,比如第三方进程等
  • SecurityManager:安全管理器,即所有与安全有关的操作都会与SecurityManager交互,且其管理着所有SubjectSubject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
  • Realm:安全数据源,当SecurityManager要验证用户身份,可以从Realm中获取用户对比其身份是否合法,也可以获取到用户的权限和验证用户操作的合法性。
基于Spring的配置方法
导入Jar包
<!-- Shiro的Jar包 -->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-core</artifactId>
	<version>1.4.0</version>
</dependency>
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-web</artifactId>
	<version>1.4.0</version>
</dependency>
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-ehcache</artifactId>
	<version>1.4.0</version>
</dependency>
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.4.0</version>
</dependency>

<!-- 项目中会使用到ehcache缓存 -->
<dependency>
	<groupId>net.sf.ehcache</groupId>
	<artifactId>ehcache-core</artifactId>
	<version>2.6.11</version>
</dependency>
配置Filter

在web.xml中配置Shiro的Filter

<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
配置CacheManager缓存管理器
<!-- 配置CacheManager 缓存管理器 -->
<bean id="cacheManager"
	 class="org.apache.shiro.cache.ehcache.EhCacheManager">
	<!-- 指定ehcache的配置文件 -->
    <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
</bean>

其中使用到ehcache的配置,ehcache-shiro.xml用默认的配置即可

<ehcache>


    <diskStore path="java.io.tmpdir/shiro-spring-sample"/>


    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />

    <cache name="shiro-activeSessionCache"
           maxElementsInMemory="10000"
           eternal="true"
           overflowToDisk="true"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="600"/>

    <cache name="org.apache.shiro.realm.SimpleAccountRealm.authorization"
           maxElementsInMemory="100"
           eternal="false"
           timeToLiveSeconds="600"
           overflowToDisk="false"/>

</ehcache>

配置Shiro的SecurityManager
<!-- 配置Shiro的SecurityManager -->
<bean id="securityManager"
	 class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="cacheManager" ref="cacheManager"/>
    <!-- 配置自定义的Realm -->
    <property name="realm" ref="myRealm"/>
</bean>

配置授权和认证的Realm
<bean id="myRealm" class="com.plf.shiro.realm.MyRealm">
	<property name="credentialsMatcher">
		<!-- 使用MD5加密  -->
		<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
			<property name="hashAlgorithmName" value="MD5"></property>
			<property name="hashIterations" value="1024"></property>
		</bean>
	</property>
</bean>

需要自定义的Realm

import java.util.HashSet;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.junit.Test;

public class MyRealm extends AuthenticatingRealm{

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken
			token) throws AuthenticationException {
		//hashCode 一样 说明token是传值过来的
		System.out.println("2"+token.hashCode());
		System.out.println("MyRealm  ----  AuthenticationInfo:"+token);
		
		//1、把AuthenticationToken强转成UsernamePasswordToken
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		
		//2、从UsernamePasswordToken中获取username
		String username = upToken.getUsername();
		
		
		//3、调用数据库方法,从数据库中查询username对应的用户记录
		System.out.println("从数据库中获取username:"+username+" 所对应的用户信息");
		
		
		//4、如果用户不存在,则可以抛出UnknownAccountException异常
		if("unknown".equals(username)){
			throw new UnknownAccountException("用户不存在");
		}
		
		
		//5、根据用户信息的情况,则可以抛出AuthenticationException异常
		if("monster".equals(username)){
			throw new LockedAccountException("用户锁定");
		}
		
		
		//6、根据用户情况,来构建AuthenticationInfo对象并返回
		//以下信息是从数据库中获取的
		//1) principal 认证的实体信息 可以是username 也可以是数据表对应的用户的实体类对象
		Object principal = username;
		 
		//2) credentials 密码
		Object credentials="";
		//123456 user
		if("user".equals(username)){
			credentials="098d2c478e9c11555ce2823231e02ec1";
		}if("admin".equals(username)){
			//123456 admin
			credentials ="038bdaf98f2037b31f1e75b5b4c9b26e";
		}
		
		//3) realmName 当前realm对象的name 调用父类的getName()方法即可
		String realmName = getName();
		
		//4) 盐值
		//这里username为唯一值所以也可以做盐值,反正就是需要随机唯一值即可
		ByteSource credentialsSalt  = ByteSource.Util.bytes(username);
		
		SimpleAuthenticationInfo info = 
			new SimpleAuthenticationInfo(principal,credentials
					,credentialsSalt,realmName);
		
		return info;
	}
	
	//主要是为了获取到MD5加密后的密码
	@Test
	public void getMD5(){
		String hashAlgorithmName ="MD5";
		Object credentials = "123456";
		Object salt = ByteSource.Util.bytes("user");
		int hashIterations = 1024;
		
		Object result = 
			new SimpleHash(hashAlgorithmName,credentials,salt,hashIterations);
		System.out.println(result);
	}
}
配置Bean的后置处理器
<!-- 配置Bean后置处理器:会自动地调用和Spring整合的各个组件的生命周期方法
			
		可以自动的来调用配置在SpringIOC容器中Shiro bean 的生命周期方法
	 -->
<bean id="lifecycleBeanPostProcessor"
    class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

启用IOC容器中使用Shiro的注解
<!--  启用IOC容器中使用shiro的注解,但必须在配置了LifecycleBeanPostProcessor之后才能使用 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" 
	depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
	<property name="securityManager" ref="securityManager"></property>
</bean>
配置Shiro的Filter
<!-- 配置 shiroFilter 该bean的id必须与web.xml文件中配置的shiro filter的name一致-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <!-- 装配securityManager -->
    <property name="securityManager" ref="securityManager"/>
    <!-- 登录页面 -->
    <property name="loginUrl" value="/shiro-login.jsp"/>
    <!-- 登录成功页面 -->
    <property name="successUrl" value="/shiro-success.jsp"/>
     <!-- 授权失败页面 -->
    <property name="unauthorizedUrl" value="/shiro-fail.jsp"/>
    
   
    <!-- 具体配置需要拦截哪些URL,以及访问对应的URL时使用Shiro的什么filter进行拦截 
    	1)anon  匿名访问
    	2)authc  认证之后(登录)才能访问的页面
    	3)logout  	登出
    	4)user    用户拦截器  ,用户已经身份验证/记住我登录即可
    	5) authcBasic Basic HTTP身份验证拦截器
    	6)roles 角色授权拦截器
    	7)perms 权限授权拦截器 验证用户是否拥有所有权限 /user/**=perms["user:create"]
    	8)port	端口拦截器/test=port[80]
    	9)rest rest风格拦截器  自动根据请求方法构建权限字符串
    	10)ssl SSL拦截器  只有请求协议是https才能通过
    	11)noSessionCreation 不创建会话拦截器
    -->
    <property name="filterChainDefinitions">
        <value>
            /shiro-logout = logout
        	# 不用认证即可访问
            /shiro-login = anon
           	
           	
            /shiro-login.jsp = anon
            /user.jsp = roles[user]
            /admin.jsp = roles[admin]
            # allow WebStart to pull the jars for the swing app:
            /*.jar = anon
            # everything else requires authentication:
            /** = authc
        </value>
    </property>
</bean>
控制器中使用
@RequestMapping("shiro-login")
public String login(@RequestParam String username
		,@RequestParam String password){
	Subject currentUser = SecurityUtils.getSubject();
	
	
	if(!currentUser.isAuthenticated()){
		//把用户名和密码封装为UsernamePasswordToken对象
		UsernamePasswordToken token = new UsernamePasswordToken(username,password);
	
		token.setRememberMe(true);
	
		try{
			System.out.println("1"+token.hashCode());
			currentUser.login(token);
		}catch(AuthenticationException e){
			System.out.println("登录失败:"+e.getMessage());
			return "shiro-fail";
		}
	}
	return "shiro-success";
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值