XSS攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。
在后端基于SpringMVC框架过滤XSS攻击还是比较简单,只需要使用@InitBinder注解即可。
org.apache.commons
commons-text
1.2
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();
}
});
}
}
通过上述的步骤即可过滤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();
}
});
}
}
注意
已经废弃
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 约束
第二步配置 即可
会将对静态资源的访问请求通过HandlerMapping映射到默认
Servlet请求处理器DefaultServletHttpRequestHandler对象。
而处理器调用了Tomcat的DefaultServlet来处理静态资源的访问请求。
其实就是将SpringMVC不能处理的请求交给tomcat。
default
*.jpg
default
*.js
default
*.css
default
*.png
在springmvc.xml中配置
在spring3.0.4版本之后,Spring中定义了专门用于处理静态资源访问请求的处理器
ResourceHttpRequestHandler,并且添加了 标签,专门用于解决
静态资源无法访问问题。
location 表示静态资源所在目录。目录可以是WEB-INF/目录以及子目录
mapping 表示对该以资源的请求
该配置会把静态资源的访问请求经HandlerMapping直接映射到
静态资源处理器对象ResourceHttpRequestHandler
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、加密和会话管理。所以它用途广泛,在大部分系统中都有应用,本博客主要讲解基于Spring的配置方法。
Shiro小巧而且简单,使用方便,很适合高效编码。
学习Shiro的基本使用可以参考:http://wiki.jikexueyuan.com/project/shiro/
github上也有相关资料参考:https://github.com/zhangkaitao/shiro-example
Subject
:表示当前的“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject
,比如第三方进程等SecurityManager
:安全管理器,即所有与安全有关的操作都会与SecurityManager
交互,且其管理着所有Subject
。Subject
代表了当前用户的安全操作,SecurityManager
则管理所有用户的安全操作。Realm
:安全数据源,当SecurityManager
要验证用户身份,可以从Realm
中获取用户对比其身份是否合法,也可以获取到用户的权限和验证用户操作的合法性。
org.apache.shiro
shiro-core
1.4.0
org.apache.shiro
shiro-web
1.4.0
org.apache.shiro
shiro-ehcache
1.4.0
org.apache.shiro
shiro-spring
1.4.0
net.sf.ehcache
ehcache-core
2.6.11
在web.xml中配置Shiro的Filter
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
shiroFilter
/*
其中使用到ehcache的配置,ehcache-shiro.xml用默认的配置即可
需要自定义的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);
}
}
/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
@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";
}