SpringSecurity简介与使用
Spring Security是一个能够为基于Spring的企业应用系统提供描述性安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC(依赖注入,也称控制反转)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
Spring Security对Web安全性的支持大量地依赖于Servlet过滤器。这些过滤器拦截进入请求,并且在应用程序处理该请求之前进行某些安全处理。 Spring Security提供有若干个过滤器,它们能够拦截Servlet请求,并将这些请求转给认证和访问决策管理器处理,从而增强安全性
接下来构建一个SpringSecurity示例:
2、新建web工程开始相关的配置工作:
2.1、web.xml的配置:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml, classpath:application-security.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <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>2.2、application-security.xml的配置:
<!-- 指定login.jsp不提供安全拦截 --> <http pattern="/login.jsp" security="none"></http> <!-- access-denied-page:访问拒绝后跳转的页面 --> <http access-denied-page="/accessDenied.jsp" auto-config="true"> <!-- <http use-expressions="true" auto-config="true" access-denied-page="/403.html"> --> <!-- form-login标签: login-page:指定登录页面,不指定默认使用spring自带的登录页面 always-use-default-target:是否使用登录成功后跳入指定网页,true使用 default-target-url:登录成功后默认进入的目标网页,必须以"/"或"http://"开头,例如:“http://www.baidu.com” <form-login login-page="/login.jsp" /> --> <form-login always-use-default-target="false" default-target-url="http://www.baidu.com"/> <!-- logout标签: logout-url:指定注销的url连接,默认为/j_spring_security_logout logout-success-url:指定注销成功后跳转的连接,默认为/ delete-cookies:删除指定的cookid集合,以逗号分割 invalidate-session:指定注销后是否是session失效,默认是true --> <logout logout-url="/myLinyLogout" logout-success-url="/loggedout.jsp" delete-cookies="JSESSIONID" invalidate-session="true"/> <remember-me /> <!-- 拦截指定URL,并指定所需要的权限 <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN"/> <intercept-url pattern="/**" access="ROLE_USER"/> --> <session-management invalid-session-url="/invalidSession.jsp"> <!-- 同时只能支持2个人在线(两个人使用同一个账户登录),第三着登录系统强行是第一个登录用户失效 <concurrency-control max-sessions="2" error-if-maximum-exceeded="false"/> --> <!-- error-if-maximum-exceeded:当登录的用户数大于max-sessions指定的数量之后,系统默认的处理行为是使前者失效(先进先失效). 默认值为false。 设置为true,则后登录的用户无法登录到系统. --> <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/> </session-management> <!-- 自定义安全拦截器 --> <custom-filter ref="mySecurityFilter" before="FILTER_SECURITY_INTERCEPTOR"/> </http> <!-- 配置过滤器 --> <beans:bean id="mySecurityFilter" class="com.liny.sys.security.util.MySecurityFilter"> <!-- 用户认证 --> <beans:property name="authenticationManager" ref="myAuthenticationManager" /> <!-- 用户校验(用户是否拥有所请求资源的权限) --> <beans:property name="accessDecisionManager" ref="myAccessDecisionManager" /> <!-- 获取请求资源所需要的权限 --> <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" /> </beans:bean> <!-- 实现了UserDetailsService的Bean --> <!-- 加载用户权限的管理器: 1、可以以配置方式定义,user-service 2、 可以以sql语句定义,jdbc-user-service 3、ldap-user-service --> <authentication-manager alias="myAuthenticationManager"> <authentication-provider user-service-ref="myUserDetailServiceImpl" /> </authentication-manager> <!-- <beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" scope="singleton"> <beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <beans:property name="url" value="jdbc:oracle:thin:@10.10.39.217:1521:orcl" /> <beans:property name="username" value="liny" /> <beans:property name="password" value="liny" /> </beans:bean> --> <beans:bean id="myAccessDecisionManager" class="com.liny.sys.security.util.MyAccessDecisionManager" /> <beans:bean id="mySecurityMetadataSource" class="com.liny.sys.security.util.MySecurityMetadataSource"/> <beans:bean id="myUserDetailServiceImpl" class="com.liny.sys.security.util.MyUserDetailServiceImpl" />3、自定义安全拦截器、用户认证、用户授权实现:
3.1、自定义安全拦截器(mySecurityFilter):
public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter{ /** 与applicationContext-security.xml里的myFilter的属性securityMetadataSource对应, 其他的两个组件,已经在AbstractSecurityInterceptor定义 **/ private FilterInvocationSecurityMetadataSource securityMetadataSource; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation filterInvocation = new FilterInvocation(request, response, chain); invoke(filterInvocation); } private void invoke(FilterInvocation fi) throws IOException, ServletException { //1.获取请求资源的权限 //执行Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object); //2.是否拥有权限 //this.accessDecisionManager.decide(authenticated, object, attributes); InterceptorStatusToken token = super.beforeInvocation(fi); //获取请求资源的权限 try { fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally { super.afterInvocation(token, null); } } @Override public Class<?> getSecureObjectClass() { return FilterInvocation.class; } @Override public SecurityMetadataSource obtainSecurityMetadataSource() { return this.securityMetadataSource; } @Override public void destroy() { } @Override public void init(FilterConfig arg0) throws ServletException { } public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() { return securityMetadataSource; } public void setSecurityMetadataSource( FilterInvocationSecurityMetadataSource securityMetadataSource) { this.securityMetadataSource = securityMetadataSource; } }3.2、获取请求资源需要的权限:
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource{ private static final Logger LOGGER = LoggerFactory.getLogger(MySecurityMetadataSource.class); private static Map<String, Collection<ConfigAttribute>> resourceMap = null; @Resource private SecurityDAO securityDAO; @Resource private MyAccessDecisionManager accessDecisionManager; public MySecurityMetadataSource() { loadResourceDefine(); } @Override public Collection<ConfigAttribute> getAttributes(Object obj)throws IllegalArgumentException { String requestUrl = ((FilterInvocation) obj).getRequestUrl(); LOGGER.debug(requestUrl); if(resourceMap == null) { loadResourceDefine(); } return resourceMap.get(requestUrl); } //加载所有资源与权限的关系,一个资源需要哪些权限 private void loadResourceDefine() { if(resourceMap == null && this.securityDAO != null) { resourceMap = new HashMap<String, Collection<ConfigAttribute>>(); List<ResourceVO> resources = this.securityDAO.getAllResource(); for (ResourceVO resource : resources) { //以权限名封装为Spring的security Object Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>(); ConfigAttribute configAttribute = new SecurityConfig(resource.getRoleName()); configAttributes.add(configAttribute); resourceMap.put(resource.getResourceURL(), configAttributes); } } } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> class1) { return true; } }3.3、请求资源的权限校验:
public class MyAccessDecisionManager implements AccessDecisionManager { private static final Logger LOGGER = LoggerFactory.getLogger(MyAccessDecisionManager.class); @Override public void decide(Authentication authentication, Object obj,Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if (configAttributes == null) { return; } // 所请求的资源拥有的权限 Iterator<ConfigAttribute> iterator = configAttributes.iterator(); while (iterator.hasNext()) { ConfigAttribute configAttribute = iterator.next(); // 访问所请求资源所需要的权限 String needPermission = configAttribute.getAttribute(); LOGGER.debug(needPermission); // 用户所拥有的权限authentication for (GrantedAuthority ga : authentication.getAuthorities()) { if (needPermission.equals(ga.getAuthority())) { return; } } } // 没有权限 throw new AccessDeniedException("没有权限访问! "); } @Override public boolean supports(ConfigAttribute configattribute) { return true; } @Override public boolean supports(Class<?> class1) { return true; } }3.4、用户身份认证:
public class MyUserDetailServiceImpl implements UserDetailsService { @Resource private SecurityDAO securityDAO; public MyUserDetailServiceImpl() { super(); } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserVO userVO = new UserVO(); userVO.setAccount(username); userVO = securityDAO.getUserByAccount(userVO); if (userVO == null) { throw new UsernameNotFoundException(username); } List<RoleVO> roles = securityDAO.getRoleByUser(userVO); Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>(); if (roles != null && roles.size() > 0) { for (RoleVO roleVO : roles) { authSet.add(new SimpleGrantedAuthority(roleVO.getRoleName())); } } UserVO objUserVO = new UserVO(userVO.getAccount(), userVO.getPassword(), userVO.isAccountNonExpired(), userVO.isCredentialsNonExpired(), userVO.isAccountNonLocked(), userVO.isEnabled(), roles, authSet); return objUserVO; } }大致的请求流程如下:
1、用户请求过滤器
2、获取请求资源需要的权限
3、进行请求资源的权限校验,如果未进行身份认证,则执行步骤4、否则执行步骤5
4、进行用户身份认证,认证成功执行步骤1
5、校验成功显示请求的资源,失败则拒绝访问请求的资源
相关推荐
与卿画眉共浮生 2020-11-13
smalllove 2020-11-03
hellowordmonkey 2020-11-02
丽丽 2020-10-30
周太郎 2020-10-28
greensomnuss 2020-10-27
职业炮灰 2020-10-16
与卿画眉共浮生 2020-10-14
feinifi 2020-10-14
feinifi 2020-10-13
yangjinpingc 2020-10-09
davis 2020-09-29
RickyIT 2020-09-27
lisongchuang 2020-09-27
tangxiong0 2020-09-03
meleto 2020-08-17
幸运小侯子 2020-08-14
YangHuiLiang 2020-08-06