之前已经在我的博客中发过security的运行流程图了,大家可以先去看看那个图再看这篇。今天我主要在这里贴出了security配置中的几个重要的类和两个xml配置文件,基本上控制权限的就是这几个文件了。因为最近都比较忙,一直没有时间发出来,导致有点忘记配置时的过程了,所以忘记了一些细节的内容,原本我打算写的详细一点的,但现在都有点忘记了,我在这里就不再一一写出来了,因为在每个文件的方法或配置里,我用注释说明了一些配置时所遇到的问题,大家可以看看,可能比较难看,因为表达可能不是很好,有些写得比较详细,导致很乱。如果大家有在网上搜索这类文章,基本上大多数配置都是差不多的,这在此之前也在网上参考了几篇文章,都写的不错,我也是参考那里配置的。我给出我看过的几个网址出来,大家可以也去看看:
1、http://wenku.baidu.com/view/ba1f791aff00bed5b9f31dc1.html 我数据库是在这篇里取的
2、
3、http://johnny-lee.iteye.com/blog/1701126
4、
按照第4篇的配置基本上都是能配置出来的
还有一个需要说明的是,在applicationContext-security.xml里我注释掉了
auto-config="true"
这个配置比较重要的,如果你不配置,security可能不会启动,我为什么又把它注释掉了,是因为当你配置了自动义的登录页面,就不用这个了,这个的作用可能是在项目启动时,如果你没有自定义的登录页面,它就会跳转到security默认的登录页面中。
我把我配置的项目放上来,大家可以去下载,我也就赚点积分,常在CSDN混,不能没有积分啊,望大家理解,下载地址:
其实之前我也上传了一个上去了,但是那个配置好像不是很完整。
我的项目是用maven搭建的,如果你配置了maven,那么就可以很轻松的运行起项目来了,
步骤:
1、在我的项目下找到database文件夹,把里面的union_ssh.sql文件导入到mysql数据库中
2、导入项目SSHMS到myEclipse中
3、在myEclipse中使用maven install,运行后,可能会稍等一下,因为它在连网下载jar包,这样就不用自己去下载jar包了
4、运行后,在登录页面输入账号、密码admin就可以登录到主界面去了,这个账号的权限是可以访问树中的所有页面;也可以使用账号、密码test登录,但这个账号权限只能用户管理这个页面
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name></display-name>
<!-- spring配置文件位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml,classpath:spring-hibernate.xml,classpath:applicationContext-security.xml</param-value>
</context-param>
<!-- spring security 过滤器, 这个的位置顺序和spring的监听器启动位置没有什么关系,可以放在spring监听器的前面,也可以放置在后面。
但一定要放在struts的过滤器前面,因为如果有自定义的登录页面,当登录时,就会跳转到了struts对应的action中,
导致无法使用spring security的验证登录了,正常情况下,应该登录时,会经过自定义的MyUsernamePasswordAuthenticationFilter类的attemptAuthentication方法进行验证。
如果验证成功,则登录成功,不再执行对应的action验证登录 ;spring security验证失败,则跳回指定登录失败的页面。
-->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- spring监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- hibernate配置 -->
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- Struts2配置 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<!-- hibernate的session启动过滤器,在url请求action时启动 ,不配置这个,url请求时无法启动hibernate的session-->
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- struts拦截的url后缀 -->
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.action</url-pattern>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>/login.jsp</welcome-file>
</welcome-file-list>
</web-app>
applicationContext-security.xml,security的主要配置文件
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- No bean named 'springSecurityFilterChain' is defined
1、 这时发布一下你的项目,查看tomcat的webapps目录下,找到你的项目文件夹的classes文件夹有没有相关的spring.xml文件存在,不存在就会报错
2、查看web.xml文件<param-value>标签有没有引入applicationContext-security.xml这个文件
-->
<!-- 不用经过spring security过滤,一般js、css都不需要过滤 -->
<http pattern="/*/js/**" security="none"/>
<http pattern="/common/js/**" security="none"/>
<http pattern="/login.jsp" security="none"/>
<http pattern="/login_logo.jpg" security="none"/>
<!-- auto-config="true" -->
<http use-expressions="true" entry-point-ref="authenticationProcessingFilterEntryPoint" >
<!-- 不再在这里对url进行权限拦截,在数据库中取出url中对应的权限
<intercept-url pattern="/**" access="ROLE_USER" />
-->
<!-- 单用户登陆 -->
<session-management>
<concurrency-control max-sessions="1"
error-if-maximum-exceeded="true" />
</session-management>
<!-- 这种自定义的登录页面,不能经过security的用户信息验证,也就等于不能取出用户的权限
<form-login login-page='/login.jsp' default-target-url="/index.jsp"/>
-->
<!-- 尝试访问没有权限的页面时跳转的页面 -->
<access-denied-handler error-page="/403.jsp"/>
<custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER" />
<custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
<!-- 检测失效的sessionId,session超时时,定位到另外一个URL -->
<session-management invalid-session-url="/sessionTimeOut.jsp" />
<!--
<custom-filter ref="logoutFilter" before="LOGOUT_FILTER"/>
-->
<logout invalidate-session="true" logout-success-url="/" logout-url="/logout"/>
</http>
<!-- 登录验证器 -->
<beans:bean id="loginFilter"
class="framework.security.login.MyUsernamePasswordAuthenticationFilter">
<!-- value="/loginUser.action"处理登录表单的action ,value值要以“/”开关,否则会报错 :
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#3' while setting bean property 'sourceList' with key [3]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#3': Cannot resolve reference to bean 'loginFilter' while setting constructor argument with key [4]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginFilter' defined in class path resource [applicationContext-security.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'filterProcessesUrl' threw exception; nested exception is java.lang.IllegalArgumentException: userAction!login.action isn't a valid redirect URL
-->
<beans:property name="filterProcessesUrl" value="/user/loginUser.action"></beans:property>
<!-- 验证成功后的处理 -->
<beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"></beans:property>
<!-- 验证失败后的处理 -->
<beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></beans:property>
<beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
<!-- 注入DAO为了查询相应的用户 -->
<beans:property name="userDao" ref="userDao"></beans:property>
</beans:bean>
<beans:bean id="loginLogAuthenticationSuccessHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/index.jsp"></beans:property>
</beans:bean>
<beans:bean id="simpleUrlAuthenticationFailureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<!-- 可以配置相应的跳转方式。属性forwardToDestination为true采用forward false为sendRedirect -->
<beans:property name="defaultFailureUrl" value="/login.jsp"></beans:property>
</beans:bean>
<!-- 认证过滤器 -->
<beans:bean id="myFilter" class="framework.security.base.MyFilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean" />
<beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" />
</beans:bean>
<!-- spring security提供的用户登录验证 ,alias的值对应上面的ref="authenticationManager" -->
<authentication-manager alias="authenticationManager">
<!--userDetailServiceImpl 获取登录的用户、用户权限 -->
<authentication-provider user-service-ref="userDetailServiceImpl" />
</authentication-manager>
<!-- 获取登录的用户、用户权限 -->
<beans:bean id="userDetailServiceImpl" class="framework.security.base.MyUserDetailsService">
<beans:property name="userDao" ref="userDao"></beans:property>
</beans:bean>
<!-- 判断是否有权限访问请求的url页面 -->
<beans:bean id="myAccessDecisionManagerBean"
class="framework.security.base.MyAccessDecisionManager">
</beans:bean>
<!-- 获取数据库中所有的url资源,读出url资源与权限的对应关系 -->
<beans:bean id="mySecurityMetadataSource"
class="framework.security.base.MySecurityMetadataSource">
</beans:bean>
<!-- 未登录的切入点 -->
<beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/sessionTimeOut.jsp"></beans:property>
</beans:bean>
</beans:beans>
MySecurityMetadataSource类,负责读取数据库中的url对应的权限
package framework.security.base;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import com.user.dao.UserDaoI;
import framework.security.util.UrlPathMatcher;
//1、2、3、4是服务器启动时调用的顺序
public class MySecurityMetadataSource implements
FilterInvocationSecurityMetadataSource {
/*
*resourceMap用static声明了,为了避免用户每请求一次都要去数据库读取资源、权限,这里只读取一次,将它保存起来
*/
private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
private UrlPathMatcher urlMatcher = new UrlPathMatcher();
private UserDaoI userDao;
// 1
//构造函数,因为服务器启动时会调用这个类,利用构造函数读取所有的url、角色
public MySecurityMetadataSource() {
//初始化,读取数据库所有的url、角色
loadResourceDefine();
}
//2
//这个方法应该是要从数据库读取数据的,这里只用来测试
/*private void loadResourceDefine() {
System.out.println("metadata : loadResourceDefine");
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
*//**
* 将这里的new SecurityConfig("ROLE_ADMIN")值改为ROLE_USER,登录成功也不允许访问index.jsp了,
* 因为在applicationContext-security.xml设置了只允许角色为ROLE_ADMIN的访问。
* <intercept-url pattern="/**" access="ROLE_ADMIN" />
*//*
ConfigAttribute ca = new SecurityConfig("ROLE_ADMIN");
atts.add(ca);
//ca为访问的权限,下面为url地址赋予ca中的权限
resourceMap.put("/i.jsp", atts);
resourceMap.put("/index.jsp", atts);
}*/
//这个方法在url请求时才会调用,服务器启动时不会执行这个方法,前提是需要在<http>标签内设置 <custom-filter>标签
//getAttributes这个方法会根据你的请求路径去获取这个路径应该是有哪些权限才可以去访问。
@Override
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
//object getRequestUrl 是获取用户请求的url地址
String url = ((FilterInvocation) object).getRequestUrl();
//resourceMap保存了loadResourceDefine方法加载进来的数据
Iterator<String> ite = resourceMap.keySet().iterator();
while (ite.hasNext()) {
//取出resourceMap中读取数据库的url地址
String resURL = ite.next();
//如果两个 url地址相同,那么将返回resourceMap中对应的权限集合,然后跳转到MyAccessDecisionManager类里的decide方法,再判断权限
if (urlMatcher.pathMatchesUrl(url, resURL)) {
return resourceMap.get(resURL); //返回对应的url地址的权限 ,resourceMap是一个主键为地址,值为权限的集合对象
}
}
//如果上面的两个url地址没有匹配,返回return null,不再调用MyAccessDecisionManager类里的decide方法进行权限验证,代表允许访问页面
return null;
}
// 4
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
// 3
@Override
public boolean supports(Class<?> clazz) {
return true;
}
private void loadResourceDefine() {
//请注意这里读取了spring的xml配置文件,如果改变了spring的xml文件名称,这里也要改变的
ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"classpath:spring.xml","classpath:spring-hibernate.xml"});
SessionFactory sessionFactory = (SessionFactory) context
.getBean("sessionFactory");
Session session=(Session) sessionFactory.openSession();
// 提取系统中的所有权限。
List<String> auNames =session.createQuery("select roleName from PubRoles").list();
session.close();//取数后将session关闭
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
for (String auth : auNames) {
ConfigAttribute ca = new SecurityConfig(auth);
//查出对应的角色的资源
Query query1=sessionFactory.openSession().createSQLQuery("SELECT resource_string FROM pub_resources WHERE resource_id IN (SELECT resource_id FROM pub_authorities_resources WHERE authority_id IN (SELECT authority_id FROM pub_roles_authorities WHERE role_id =( SELECT role_id FROM pub_roles WHERE role_name='"+auth+"')))");
List<String> list = query1.list();
for (String res : list) {
String url = res;
// * 判断资源文件和权限的对应关系,如果已经存在相关的资源url,则要通过该url为key提取出权限集合,将权限增加到权限集合中。
if (resourceMap.containsKey(url)) {
Collection<ConfigAttribute> value = resourceMap.get(url); //取出这个url的权限集合
value.add(ca);
resourceMap.put(url, value);
} else {
Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
atts.add(ca);
resourceMap.put(url, atts);
}
}
}
}
public UserDaoI getUserDao() {
return userDao;
}
public void setUserDao(UserDaoI userDao) {
this.userDao = userDao;
}
}
MyFilterSecurityInterceptor类,负责过滤url请求
package framework.security.base;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
//implements Filter是servlet的filter类
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(arg0, arg1, arg2);
invoke(fi);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
//自定义的方法
public void invoke(FilterInvocation fi) throws IOException, ServletException {
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return securityMetadataSource;
}
public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
}
MyUserDetailsService类,用户登录验证的类
package framework.security.base;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.memory.UserMap;
import com.user.dao.UserDaoI;
import com.user.dao.impl.UserDaoImpl;
import com.user.model.PubUsers;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
public class MyUserDetailsService implements UserDetailsService{
private UserDaoImpl userDao;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
PubUsers users=userDao.userInfo(username);
/* if(users.getAccount()==null){
throw new AccessDeniedException("账号或密码错误!");
}*/
/*不要使用GrantedAuthorityImpl,官网说这个已过期了,
* SimpleGrantedAuthority代替GrantedAuthorityImpl,赋予一个角色(即权限)
*
* */
List<String> list = userDao.findAuthByUsername(username);
for (int i = 0; i < list.size(); i++) {
auths.add(new SimpleGrantedAuthority(list.get(i)));
//auths.add(new GrantedAuthorityImpl(list.get(i)));
}
boolean enables = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
//System.out.println(users.getUserName());
//System.out.println(users.getUserPassword());
//封装成spring security的User
User userdetail = new User(users.getUserAccount(), users.getUserPassword(), enables, accountNonExpired, credentialsNonExpired, accountNonLocked, auths);
return userdetail;
}
public UserDaoImpl getUserDao() {
return userDao;
}
public void setUserDao(UserDaoImpl userDao) {
this.userDao = userDao;
}
}
MyAccessDecisionManager类,负责权限的控制,如果请求的url在权限集合中有这个url对应的值,则放行。注:如果数据库中没有对这个url定义访问的权限,默认是会被放行的
package framework.security.base;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
public class MyAccessDecisionManager implements AccessDecisionManager {
//In this method, need to compare authentication with configAttributes.
// 1, A object is a URL, a filter was find permission configuration by this URL, and pass to here.
// 2, Check authentication has attribute in permission configuration (configAttributes)
// 3, If not match corresponding authentication, throw a AccessDeniedException.
//这个方法在url请求时才会调用,服务器启动时不会执行这个方法,前提是需要在<http>标签内设置 <custom-filter>标签
/*
* 参数说明:
* 1、configAttributes 装载了请求的url允许的角色数组 。这里是从MySecurityMetadataSource里的loadResourceDefine方法里的atts对象取出的角色数据赋予给了configAttributes对象
* 2、authentication 装载了从数据库读出来的角色 数据。这里是从MyUserDetailsService里的loadUserByUsername方法里的auths对象的值传过来给 authentication 对象
*
* */
@Override
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
/*
* authentication装载了用户的信息数据,其中有角色。是MyUserDetailsService里的loadUserByUsername方法的userdetail对象传过来的
* userdetail一共有7个参数(下面打印出来的数据可对应一下security的User类,这个类可以看到有寻7个参数),
* 最后一个是用来保存角色数据的,如果角色为空,将无权访问页面。
* 看到下面的打印数据, Granted Authorities: ROLE_ADMIN,ROLE_ADMIN 就是角色了。
* 如果显示Not granted any authorities,则说明userdetail的最后一个参数为空,没有传送角色的值过来
*
* 打印出的数据:
* auth:org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bf5fbace:
Principal: org.springframework.security.core.userdetails.User@c20:
Username: aa;
Password: [PROTECTED];
Enabled: true;
AccountNonExpired: true;
credentialsNonExpired: true;
AccountNonLocked: true;
Granted Authorities: ROLE_ADMIN;
Credentials: [PROTECTED];
Authenticated: true;
Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffc7f0c: RemoteIpAddress: 127.0.0.1;
SessionId: 0952B3F9F18222DCCD0ECE39D039F900;
Granted Authorities: ROLE_ADMIN
*/
if(configAttributes == null){
return;
}
//System.out.println(object.toString()); //object is a URL.
Iterator<ConfigAttribute> ite=configAttributes.iterator();
while(ite.hasNext()){
ConfigAttribute ca=ite.next();
String needRole=((SecurityConfig)ca).getAttribute();
for(GrantedAuthority ga:authentication.getAuthorities()){
//判断两个请求的url页面的权限和用户的权限是否相同,如相同,允许访问
if(needRole.equals(ga.getAuthority())){
/* <intercept-url pattern="/**" access="ROLE_ADMIN" />
* 如果applicationContext-security.xml的http标签里面有这种配置,
* 在needRole为ROLE_USER时,即使needRole和ga.getAuthority权限匹配了,但权限是ROLE_USER,即使执行了return,
* 还是会无法访问请求的url页面,因为最终都是以http标签里的权限来拦截,即只能在权限为 ROLE_ADMIN才可访问
*/
return;
}
}
}
//如果上面的needRole和ga.getAuthority两个权限没有匹配,将不允许访问
throw new AccessDeniedException("Access Denied");
}
/*
* * 这个 supports(ConfigAttribute attribute) 方法在启动的时候被
* AbstractSecurityInterceptor调用,来决定AccessDecisionManager
* 是否可以执行传递ConfigAttribute。 supports(Class)方法被安全拦截器实现调用,
* 包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型。
*
* */
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
//return false;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
//return false;
}
}
MyUsernamePasswordAuthenticationFilter类,自定义的登录类
package framework.security.login;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.login.AccountExpiredException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.ServletActionContext;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.user.dao.UserDaoI;
import com.user.model.PubUsers;
import framework.util.MD5Utils;
public class MyUsernamePasswordAuthenticationFilter extends
UsernamePasswordAuthenticationFilter {
public static final String VALIDATE_CODE = "validateCode";
public static final String USERNAME = "userAccount";
public static final String PASSWORD = "userPassword";
private int showCheckCode = 0;
public int getShowCheckCode() {
return showCheckCode;
}
public void setShowCheckCode(int showCheckCode) {
this.showCheckCode = showCheckCode;
}
private MD5Utils md5 =new MD5Utils();
private UserDaoI userDao;
public UserDaoI getUserDao() {
return userDao;
}
public void setUserDao(UserDaoI userDao) {
this.userDao = userDao;
}
public MD5Utils getMd5() {
return md5;
}
public void setMd5(MD5Utils md5) {
this.md5 = md5;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
//检测验证码
checkValidateCode(request);
String username = obtainUsername(request);
String password = obtainPassword(request);
//验证用户账号与密码是否对应
username = username.trim();
PubUsers users=userDao.userInfo(username);
HttpSession session = request.getSession();
session = request.getSession(false);//false代表不创建新的session,直接获取当前的session
//将用户名存进session,如果登录成功,显示在主页
session.setAttribute("login_account",username);
if(users == null) {
session.setAttribute("showCheckCode" ,"1" );
session.setAttribute("SECURITY_LOGIN_EXCEPTION" ,
"用户名或密码错误!" );
throw new AuthenticationServiceException("用户名或密码错误!");
}else if(users.getUserPassword()=="" || users.getUserPassword()==null){
session.setAttribute("showCheckCode" ,"1" );
session.setAttribute("SECURITY_LOGIN_EXCEPTION" ,
"用户名或密码错误!" );
throw new AuthenticationServiceException("用户名或密码错误!");
}else if(!users.getUserPassword().equals(md5.MD5Encode(password))){// password加密后再进行验证
session.setAttribute("showCheckCode" ,"1" );
session.setAttribute("SECURITY_LOGIN_EXCEPTION" ,
"用户名或密码错误!" );
throw new AuthenticationServiceException("用户名或密码错误!");
}else{
if(session.getAttribute("showCheckCode")=="1"){
session.setAttribute("showCheckCode" , "0" );
}
}
//UsernamePasswordAuthenticationToken实现 Authentication
//这里要注意了,我第二个参数是用自己的md5加密了密码再去传参的,因为我的密码都是加密后存进数据库的。
//如果这里不加密,那么和在数据库取出来的不匹配,最终即使登录账号和密码都正确,也将无法登录成功。
//因为在AbstractUserDetailsAuthenticationProvider里还会对用户和密码验证,分别是
//user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);//这个通过才能顺利通过
//另一个是 additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);//如果retrieveUser方法验证不通过,将无法访问
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, md5.MD5Encode(password));
// Place the last username attempted into HttpSession for views
// 允许子类设置详细属性
setDetails(request, authRequest);
// 运行UserDetailsService的loadUserByUsername 再次封装Authentication
return this.getAuthenticationManager().authenticate(authRequest);
}
protected void checkValidateCode(HttpServletRequest request) {
HttpSession session = request.getSession();
String sessionValidateCode = obtainSessionValidateCode(session);
if( session.getAttribute("showCheckCode")=="1"){
//让上一次的验证码失效
session.setAttribute(VALIDATE_CODE, null);
String validateCodeParameter = obtainValidateCodeParameter(request);
//判断输入的验证码和保存在session中的验证码是否相同,这里不区分大小写进行验证
if (StringUtils.isEmpty(validateCodeParameter) || !sessionValidateCode.equalsIgnoreCase(validateCodeParameter)) {
session = request.getSession(false);//false代表不创建新的session,直接获取当前的session
session.setAttribute("SECURITY_LOGIN_EXCEPTION" ,
"验证码错误" );
throw new AuthenticationServiceException("验证码错误!");
}
}
}
private String obtainValidateCodeParameter(HttpServletRequest request) {
Object obj = request.getParameter(VALIDATE_CODE);
return null == obj ? "" : obj.toString();
}
protected String obtainSessionValidateCode(HttpSession session) {
Object obj = session.getAttribute(VALIDATE_CODE);
return null == obj ? "" : obj.toString();
}
@Override
protected String obtainUsername(HttpServletRequest request) {
Object obj = request.getParameter(USERNAME);
return null == obj ? "" : obj.toString();
}
@Override
protected String obtainPassword(HttpServletRequest request) {
Object obj = request.getParameter(PASSWORD);
return null == obj ? "" : obj.toString();
}
}