配色: 字号:
Spring Security学习一
2016-09-14 | 阅:  转:  |  分享 
  
SpringSecurity学习一-控制同个帐号当前只能有一个登录

Date:2016-09-14

Author:Kagula

Envronment:

[1]JDK1.7.0_79

[2]apache-tomcat-7.0.68

[3]EclipseMars2



Introduction:

据《springsecurity的原理及教程》能实现

[a]踢出其它地方登录的这个帐号。

[b]或者已经登录了,就不允许再登录。

理论部份参考资料[1]《springsecurity的原理及教程》

这里只贴代码





示例由6个class、6个jsp、三个配置文件组成

web.xml





[html]viewplaincopy在CODE上查看代码片派生到我的代码片




xmlns="http://java.sun.com/xml/ns/javaee"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"

id="schedule-console"version="3.0">



ArchetypeCreatedWebApplication







contextConfigLocation

classpath:securityConfig.xml









springSecurityFilterChain

org.springframework.web.filter.DelegatingFilterProxy







springSecurityFilterChain

/









org.springframework.web.context.ContextLoaderListener









index.jsp











pom.xml



[html]viewplaincopy在CODE上查看代码片派生到我的代码片


xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/maven-v4_0_0.xsd">

4.0.0

com.nuoke

testSpringSecurity

war

0.0.1-SNAPSHOT

testSpringSecurityMavenWebapp

http://maven.apache.org





UTF-8

UTF-8

3.1.2.RELEASE









org.springframework

spring-core

${spring.version}









org.springframework

spring-beans

${spring.version}







org.springframework

spring-webmvc

${spring.version}







org.springframework.security

spring-security-core

${spring.version}







org.springframework.security

spring-security-web

${spring.version}







org.springframework.security

spring-security-config

${spring.version}







org.springframework.security

spring-security-taglibs

${spring.version}









testSpringSecurity







org.apache.maven.plugins

maven-compiler-plugin

3.0



1.7

1.7













securityConfig.xml



这个文件新建的时候放在“src/main/resouces”节点下



[html]viewplaincopy在CODE上查看代码片派生到我的代码片




xmlns:b="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security-3.1.xsd">


























error-if-maximum-exceeded="false"/>






















class="com.nuoke.MyFilterSecurityInterceptor">


































class="com.nuoke.MyAccessDecisionManager">








class="com.nuoke.MyInvocationSecurityMetadataSource"/>











java文件有6个,其中前面四个是关键。

MyAccessDecisionManager.java



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

packagecom.nuoke;



importjava.util.Collection;

importjava.util.Iterator;



importorg.springframework.security.access.AccessDecisionManager;

importorg.springframework.security.access.AccessDeniedException;

importorg.springframework.security.access.ConfigAttribute;

importorg.springframework.security.access.SecurityConfig;

importorg.springframework.security.authentication.InsufficientAuthenticationException;

importorg.springframework.security.core.Authentication;

importorg.springframework.security.core.GrantedAuthority;



publicclassMyAccessDecisionManagerimplementsAccessDecisionManager{

//检查用户是否够权限访问资源

//参数authentication是从spring的全局缓存SecurityContextHolder中拿到的,里面是用户的权限信息

//参数object是url

//参数configAttributes所需的权限

@Override

publicvoiddecide(Authenticationauthentication,

Objectobject,

CollectionconfigAttributes)

throwsAccessDeniedException,InsufficientAuthenticationException

{

if(configAttributes==null){

return;

}



Iteratorite=configAttributes.iterator();

while(ite.hasNext()){

ConfigAttributeca=ite.next();

StringneedRole=((SecurityConfig)ca).getAttribute();

for(GrantedAuthorityga:authentication.getAuthorities()){

if(needRole.equals(ga.getAuthority())){

return;

}

}

}



//注意:执行这里,后台是会抛异常的,但是界面会跳转到所配的access-denied-page页面

thrownewAccessDeniedException("noright");

}



publicbooleansupports(ConfigAttributeattribute){

returntrue;

}



@Override

publicbooleansupports(Classarg0){

returntrue;

}

}





MyFilterSecurityInterceptor.java



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

packagecom.nuoke;



importjava.io.IOException;



importjavax.servlet.Filter;

importjavax.servlet.FilterChain;

importjavax.servlet.FilterConfig;

importjavax.servlet.ServletException;

importjavax.servlet.ServletRequest;

importjavax.servlet.ServletResponse;



importorg.springframework.security.access.SecurityMetadataSource;

importorg.springframework.security.access.intercept.AbstractSecurityInterceptor;

importorg.springframework.security.access.intercept.InterceptorStatusToken;

importorg.springframework.security.web.FilterInvocation;

importorg.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;



/

继承AbstractSecurityInterceptor、实现Filter是必须的。

首先,登陆后,每次访问资源都会被这个拦截器拦截,会执行doFilter这个方法,

这个方法调用了invoke方法,其中fi断点显示是一个url(可能重写了toString方法吧,但是里面还有一些方法的),最重要的是beforeInvocation这个方法,

它首先会调用MyInvocationSecurityMetadataSource类的getAttributes方法获取被拦截url所需的权限,

then调用MyAccessDecisionManager类decide方法判断用户是否够权限。弄完这一切就会执行下一个拦截器.

/

publicclassMyFilterSecurityInterceptorextendsAbstractSecurityInterceptorimplementsFilter{

//配置文件注入

privateFilterInvocationSecurityMetadataSourcesecurityMetadataSource;



//登陆后,每次访问资源都通过这个拦截器拦截

publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)

throwsIOException,ServletException{

FilterInvocationfi=newFilterInvocation(request,response,chain);

invoke(fi);

}



publicFilterInvocationSecurityMetadataSourcegetSecurityMetadataSource(){

returnthis.securityMetadataSource;

}



@Override

publicClassgetSecureObjectClass(){

returnFilterInvocation.class;

}



publicvoidinvoke(FilterInvocationfi)throwsIOException,ServletException{

//fi里面有一个被拦截的url

//里面调用MyInvocationSecurityMetadataSource的getAttributes(Objectobject)这个方法获取fi对应的所有权限

//再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够

InterceptorStatusTokentoken=super.beforeInvocation(fi);

try{

//执行下一个拦截器

fi.getChain().doFilter(fi.getRequest(),fi.getResponse());

}finally{

super.afterInvocation(token,null);

}

}



publicSecurityMetadataSourceobtainSecurityMetadataSource(){

returnthis.securityMetadataSource;

}



publicvoidsetSecurityMetadataSource(

FilterInvocationSecurityMetadataSourcenewSource)

{

this.securityMetadataSource=newSource;

}



publicvoiddestroy(){



}



publicvoidinit(FilterConfigarg0)throwsServletException{

}

}





MyInvocationSecurityMetadataSource.java



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

packagecom.nuoke;



importjava.util.ArrayList;

importjava.util.Collection;

importjava.util.HashMap;

importjava.util.Iterator;

importjava.util.Map;



importorg.springframework.security.access.ConfigAttribute;

importorg.springframework.security.access.SecurityConfig;

importorg.springframework.security.web.FilterInvocation;

importorg.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;



publicclassMyInvocationSecurityMetadataSource

implementsFilterInvocationSecurityMetadataSource{

privateUrlMatcherurlMatcher=newAntUrlPathMatcher();

privatestaticMap>resourceMap=null;



//tomcat启动时实例化一次

publicMyInvocationSecurityMetadataSource(){

//这个类的实例化只在web服务器启动时调用一次,那就是说loadResourceDefine方法只会调用一次

//所以只适合页面的权限不再更改的情况。

loadResourceDefine();

}



//tomcat开启时加载一次,加载所有url和权限(或角色)的对应关系

privatevoidloadResourceDefine(){

resourceMap=newHashMap>();



//需要ROLE_USER角色登录后才能访问的页面。

Collectionatts=newArrayList();

ConfigAttributeca=newSecurityConfig("ROLE_USER");

atts.add(ca);

resourceMap.put("/index.jsp",atts);

resourceMap.put("/admin.jsp",atts);



//任何用户都没有进入/other.jsp权限

Collectionattsno=newArrayList();

ConfigAttributecano=newSecurityConfig("ROLE_NO");

attsno.add(cano);

resourceMap.put("/accessDenied.jsp",attsno);



//当url有交集时,就有可能漏掉一些角色

}



//参数是要访问的url,返回这个url对于的所有权限(或角色)

publicCollectiongetAttributes(Objectobject)

throwsIllegalArgumentException{

//将参数转为url

Stringurl=((FilterInvocation)object).getRequestUrl();

Iteratorite=resourceMap.keySet().iterator();

while(ite.hasNext()){

StringresURL=ite.next();

if(urlMatcher.pathMatchesUrl(resURL,url)){

returnresourceMap.get(resURL);

}

}

returnnull;

}



@Override

publicbooleansupports(Classarg0){

returntrue;

}



publicCollectiongetAllConfigAttributes(){

returnnull;

}

}





MyUserDetailService.java



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

packagecom.nuoke;



importjava.util.ArrayList;

importjava.util.Collection;



importorg.springframework.dao.DataAccessException;

importorg.springframework.security.core.GrantedAuthority;

importorg.springframework.security.core.authority.GrantedAuthorityImpl;

importorg.springframework.security.core.authority.SimpleGrantedAuthority;

importorg.springframework.security.core.userdetails.User;

importorg.springframework.security.core.userdetails.UserDetails;

importorg.springframework.security.core.userdetails.UserDetailsService;

importorg.springframework.security.core.userdetails.UsernameNotFoundException;



publicclassMyUserDetailServiceimplementsUserDetailsService{

//登陆验证时,通过username获取用户的所有权限信息,

//并返回User放到spring的全局缓存SecurityContextHolder中,以供授权器使用

publicUserDetailsloadUserByUsername(Stringusername)

throwsUsernameNotFoundException,DataAccessException{

Collectionauths=newArrayList();



SimpleGrantedAuthorityauth2=newSimpleGrantedAuthority("ROLE_ADMIN");

SimpleGrantedAuthorityauth1=newSimpleGrantedAuthority("ROLE_USER");



if(username.equals("admin")){

auths=newArrayList();

auths.add(auth1);

auths.add(auth2);

}



//第二个参数是密码。

Useruser=newUser(username,"123",true,true,true,true,auths);

returnuser;

}

}





UrlMatcher.java



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

packagecom.nuoke;



publicinterfaceUrlMatcher{

Objectcompile(StringparamString);

booleanpathMatchesUrl(ObjectparamObject,StringparamString);

StringgetUnivewww.shanxiwang.netrsalMatchPattern();

booleanrequiresLowerCaseUrl();

}







AntUrlPathMatcher.java



[java]viewplaincopy在CODE上查看代码片派生到我的代码片

packagecom.nuoke;



importorg.springframework.util.AntPathMatcher;

importorg.springframework.util.PathMatcher;



publicclassAntUrlPathMatcherimplementsUrlMatcher{

privatebooleanrequiresLowerCaseUrl;

privatePathMatcherpathMatcher;



publicAntUrlPathMatcher(){

this(true);

}



publicAntUrlPathMatcher(booleanrequiresLowerCaseUrl)

{

this.requiresLowerCaseUrl=true;

this.pathMatcher=newAntPathMatcher();

this.requiresLowerCaseUrl=requiresLowerCaseUrl;

}



publicObjectcompile(Stringpath){

if(this.requiresLowerCaseUrl){

returnpath.toLowerCase();

}

returnpath;

}



publicvoidsetRequiresLowerCaseUrl(booleanrequiresLowerCaseUrl){

this.requiresLowerCaseUrl=requiresLowerCaseUrl;

}



publicbooleanpathMatchesUrl(Objectpath,Stringurl){

if(("/".equals(path))||("".equals(path))){

returntrue;

}

returnthis.pathMatcher.match((String)path,url);

}



publicStringgetUniversalMatchPattern(){

return"/";

}



publicbooleanrequiresLowerCaseUrl(){

returnthis.requiresLowerCaseUrl;

}



publicStringtoString(){

returnsuper.getClass().getName()+"[requiresLowerCase=''"+this.requiresLowerCaseUrl+"'']";

}

}







jsp文件共有6个。

accessDenied.jsp



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

<%@pagelanguage="java"import="java.util."pageEncoding="utf-8"%>







MyJSP''accessDenied.jsp''startingpage





accessDenied.












admin.jsp



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

<%@pagelanguage="java"import="java.util.,java.text."pageEncoding="utf-8"%>







MyJSP''admin.jsp''startingpage





欢迎来到管理员页面.




<%

Datedate=newDate();

SimpleDateFormatt=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");

Stringtime=t.format(date);

%>




当前时间:<%=time%>









index.jsp



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

<%@pagelanguage="java"import="java.util.,java.text."pageEncoding="UTF-8"%>

<%@pagecontentType="text/html;charset=utf-8"%>

<%@taglibprefix="sec"uri="http://www.springframework.org/security/tags"%>













MyJSP''index.jsp''startingpage







这是首页

欢迎

!






进入admin页面

进入其它页面



<%

Datedate=newDate();

SimpleDateFormatt=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");

Stringtime=t.format(date);

%>




当前时间:<%=time%>













login.jsp



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

<%@pagelanguage="java"import="java.util."pageEncoding="UTF-8"%>







登录

































用户:
密码:












other.jsp



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

<%@pagelanguage="java"import="java.util."pageEncoding="UTF-8"%>

<%

Stringpath=request.getContextPath();

StringbasePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>









">



MyJSP''other.jsp''startingpage























这里是Other页面,不需要任何权限就可以访问











sessionexpired.jsp



[html]viewplaincopy在CODE上查看代码片派生到我的代码片

<%@pagelanguage="java"import="java.util."pageEncoding="UTF-8"%>

<%

Stringpath=request.getContextPath();

StringbasePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>









">



session过期







session过期













备注:

Q如何在页面上判断是否登录?springsecurity3.1

springsecurity登录成功后,会把用户名信息保存在保存在session里面,

其中key为:SPRING_SECURITY_LAST_USERNAME,那么你只需要判断session中这个key是否有值即可

献花(0)
+1
(本文系网络学习天...首藏)