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