• 基于SpringMVC实现常见功能


    基于SpringMVC实现常见功能

    防止XSS攻击

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

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

    导入Jar包
    
        org.apache.commons
        commons-text
        1.2
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    过滤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();
    			}
    		});
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    继承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();
    			}
    		});
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    配置
    
        
            
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意

    已经废弃
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
    
    建议使用
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    静态资源的配置

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

    第一种
    在springmvc中配置:
    
    第一步需要 http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd 约束
    
    第二步配置 即可
    
    会将对静态资源的访问请求通过HandlerMapping映射到默认
    Servlet请求处理器DefaultServletHttpRequestHandler对象。
    而处理器调用了Tomcat的DefaultServlet来处理静态资源的访问请求。
    
    其实就是将SpringMVC不能处理的请求交给tomcat。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    第二种
    
      	default
      	*.jpg
      
      
      	default
      	*.js
      
      
      	default
      	*.css
      
      
      	default
      	*.png
      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    第三种
    在springmvc.xml中配置
    
    	
    在spring3.0.4版本之后,Spring中定义了专门用于处理静态资源访问请求的处理器
    ResourceHttpRequestHandler,并且添加了标签,专门用于解决
    静态资源无法访问问题。
    
    location 表示静态资源所在目录。目录可以是WEB-INF/目录以及子目录
    mapping 表示对该以资源的请求
    
    该配置会把静态资源的访问请求经HandlerMapping直接映射到
    静态资源处理器对象ResourceHttpRequestHandler
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    集成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包
    
    
    	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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    配置Filter

    在web.xml中配置Shiro的Filter

    
        shiroFilter
        org.springframework.web.filter.DelegatingFilterProxy
        
            targetFilterLifecycle
            true
        
    
    
    
        shiroFilter
        /*
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    配置CacheManager缓存管理器
    
    
    	
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    
    
    
        
    
    
        
    
        
    
        
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    配置Shiro的SecurityManager
    
    
        
        
        
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    配置授权和认证的Realm
    
    	
    		
    		
    			
    			
    		
    	
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    需要自定义的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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    配置Bean的后置处理器
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    启用IOC容器中使用Shiro的注解
    
    
    
    	
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    配置Shiro的Filter
    
    
        
        
        
        
        
        
         
        
        
       
        
        
            
                /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
            
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    控制器中使用
    @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";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    Java面试题08
    你是否想知道如何应对高并发?Go语言为你提供了答案!
    数字信号处理MATLAB作业
    c++day3
    flutter 运行ios模拟器报错
    js数组对象中根据某个相同的字段找到其他对象中有值的数据
    DTCloud 复杂字段类型
    蓝桥杯第一天
    强化学习问题(五)--- ImportError: sys.meta_path is None, Python is likely shutting down
    由于找不到vcruntime140_1.dll文件的解决方法,带你了解vcruntime140_1.dll这个dll
  • 原文地址:https://blog.csdn.net/flash_love/article/details/132685011