您的位置:首页 > 编程语言 > Java开发

spring security4.2.3 登录权限资源控制框架

2018-10-09 18:05 85 查看

1. 原理:使用众多的拦截器对url拦截,以此来管理权限。但是这么多拦截器,笔者不可能对其一一来讲,主要讲里面核心流程的两个。
首先,权限管理离不开登陆验证的,所以登陆验证拦截器AuthenticationProcessingFilter要讲;
还有就是对访问的资源管理吧,所以资源管理拦截器AbstractSecurityInterceptor要讲;
但拦截器里面的实现需要一些组件来实现,所以就有了AuthenticationManager、accessDecisionManager等组件来支撑。
现在先大概过一遍整个流程,用户登陆,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,而且AuthenticationManager会调用ProviderManager来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。

访问资源(即授权管理),访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,在调用授权管理器AccessDecisionManager,这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等),如果权限足够,则返回,权限不够则报错并调用权限不足页面。

直接上代码

[code]1.springsecurity.xml
<!-- 用户的密码加密或解密 -->
<bean id="passwordEncoder" class="com.whggbd.bsz.secutiry.MyPasswordEncoder" />

<bean id="authenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService" />
<property name="hideUserNotFoundExceptions" value="false" />
<property name="passwordEncoder" ref="passwordEncoder" />
</bean>

<!-- 认证配置, 使用userDetailsService提供的用户信息 -->
<s:authentication-manager alias="authenticationManager">
<s:authentication-provider ref="authenticationProvider" />
</s:authentication-manager>

<!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源。 -->
<bean id="accessDecisionManager" class="com.whggbd.bsz.secutiry.MyAccessDecisionManager" />

<!-- 一个自定义的filter,必须包含authenticationManager, accessDecisionManager,securityMetadataSource三个属性。 -->
<bean id="myFilter" class="com.whggbd.bsz.secutiry.MyFilterSecurityInterceptor">
<!-- 资源数据源 -->
<property name="securityMetadataSource" ref="mySecurityMetadataSource" />
<!-- 认证管理器 -->
<property name="authenticationManager" ref="authenticationManager" />
<!-- 访问决策器 -->
<property name="accessDecisionManager" ref="accessDecisionManager" />
</bean>

<!-- 重写登陆验证 -->
<bean id="loginFilter"
class="com.whggbd.bsz.secutiry.MyUsernamePasswordAuthenticationFilter">
<!-- 处理登陆的Action -->
<property name="filterProcessesUrl" value="/j_spring_security_check"></property>
<!-- 验证成功后的跳转 -->
<property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"></property>
<!-- 验证失败后的处理 -->
<property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></property>

<!-- <property name="sessionAuthenticationStrategy" ref="sessionAuthenticationStrategy" /> -->
<!-- 认证管理 -->
<property name="authenticationManager" ref="authenticationManager"></property>
</bean>
<!-- 登陆成功 -->
<bean id="loginLogAuthenticationSuccessHandler"
class="com.whggbd.bsz.secutiry.MySimpleUrlAuthenticationSuccessHandler">
<!--<property name="alwaysUseDefaultTargetUrl" value="true"/> <property
name="defaultTargetUrl" value="/views/desktop.jsp" /> <property name="targetUrlParameter"
value="redirectTo" /> -->
</bean>
<!-- 登陆失败 -->
<bean id="simpleUrlAuthenticationFailureHandler"
class="com.whggbd.bsz.secutiry.MySimpleUrlAuthenticationFailureHandler">
<!-- 可以配置相应的跳转方式。属性forwardToDestination为true采用forward false为sendRedirect -->
<!-- <property name="defaultFailureUrl" value="/failure"></property> -->
</bean>

<!-- Spring Security 认证切入点 -->
<bean id="loginUrlEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg name="loginFormUrl" value="/views/index.jsp" />
</bean>

<!-- 不要过滤图片等静态资源,其中**代表可以跨越目录,*不可以跨越目录。 -->
<s:http pattern="/**/*.ico" security="none" />
<s:http pattern="/**/*.svg" security="none" />
<s:http pattern="/**/*.jpg" security="none" />
<s:http pattern="/**/*.png" security="none" />
<s:http pattern="/**/*.gif" security="none" />
<s:http pattern="/**/*.css" security="none" />
<s:http pattern="/**/*.css.map" security="none" />
<s:http pattern="/**/*.js" security="none" />
<s:http pattern="/views/index.jsp" security="none" />
<s:http pattern="/views/app/login/login.jsp" security="none" />
<s:http pattern="/**/*.docx" security="none" />
<s:http pattern="/**/*.doc" security="none" />
<s:http pattern="/views/template/alarmInfo1.jsp" security="none" />
<s:http pattern="/views/app/overview/surveyStationSimpleVer.jsp"
security="none" />

<s:http auto-config="false"  use-expressions="true"
entry-point-ref="loginUrlEntryPoint">
<s:headers>
<s:frame-options disabled="true"/>
</s:headers>

<!-- 权限判断、处理的filter链 -->
<s:custom-filter before="FILTER_SECURITY_INTERCEPTOR"
ref="myFilter" />

<!-- 处理用户登陆时,用户名和密码判断的filter(重要) -->
<s:custom-filter position="FORM_LOGIN_FILTER" ref="loginFilter" />

<s:session-management invalid-session-url="/sessionTimeout" />

<!-- 登出 -->

<!-- <s:logout delete-cookies="JSESSIONID" logout-url="/logout"
invalidate-session="true" logout-success-url="/" /> -->
<s:csrf disabled="true" />
</s:http>
2.UserDetail.class
package com.whggbd.bsz.secutiry;
import com.whggbd.bsz.entity.TSysUser;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.whggbd.bsz.entity.TSysUser;

/**
* 权限管理使用user对象:必须实现userDetails接口
*/
public class UserDetail implements UserDetails {
private Collection<GrantedAuthority> authorities;
private TSysUser user;

//    private String password;
//    private String username;
private boolean isAccountNonExpired;
private boolean isAccountNonLocked;
private boolean isCredentialsNonExpired;
private boolean isEnabled;

/** default constructor */
public UserDetail() {
}

public UserDetail(TSysUser user,
boolean isAccountNonExpired, boolean isAccountNonLocked,
boolean isCredentialsNonExpired, boolean isEnabled, Collection<GrantedAuthority> authorities) {
this.user=user;
//      this.username = username;
//      this.password = password;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;
this.authorities = authorities;
}

// Constructors

public Collection<GrantedAuthority> getAuthorities() {
return authorities;
}

public void setAuthorities(Collection<GrantedAuthority> authorities) {
this.authorities = authorities;
}

public String getPassword() {
return user.getUSER_PASSWORD();
}

public TSysUser getUser() {
return user;
}

public void setUser(TSysUser user) {
this.user = user;
}

public String getUsername() {
return user.getUSER_NAME();
}

public boolean isAccountNonExpired() {
return isAccountNonExpired;
}

public void setAccountNonExpired(boolean isAccountNonExpired) {
this.isAccountNonExpired = isAccountNonExpired;
}

public boolean isAccountNonLocked() {
return isAccountNonLocked;
}

public void setAccountNonLocked(boolean isAccountNonLocked) {
this.isAccountNonLocked = isAccountNonLocked;
}

public boolean isCredentialsNonExpired() {
return isCredentialsNonExpired;
}

public void setCredentialsNonExpired(boolean isCredentialsNonExpired) {
this.isCredentialsNonExpired = isCredentialsNonExpired;
}

public boolean isEnabled() {
return isEnabled;
}

public void setEnabled(boolean isEnabled) {
this.isEnabled = isEnabled;
}

//    @Override
//    public boolean equals(Object obj) {
//        System.out.println("进入equals方法");
//        if (obj instanceof UserDetail) {
//          UserDetail another = (UserDetail)obj;
//            return this.getUsername().equals(another.getUsername());
//        }else{
//          return false;
//        }
//    }
//
//    @Override
//    public int hashCode() {
//        System.out.println("进入hashCode方法");
//        return this.getUsername().hashCode();
//    }

}

3.MyUsernamePasswordAuthenticationFilter.class
package com.whggbd.bsz.secutiry;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
* 重写用户名密码验证部分,加入了验证码的验证
*/
public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException
{
System.out.println("MyUsernamePasswordAuthenticationFilter.attemptAuthentication()............");
try
{
Authentication authentication = super.attemptAuthentication(request, response);
return authentication;
} catch (UsernameNotFoundException e)
{
throw new AuthenticationServiceException(e.getMessage());
} catch (Exception e)
{
throw new AuthenticationServiceException("用户名或密码不正确!");
}
}
}

3.ETUserDetailService.class
package com.whggbd.bsz.secutiry;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import o
20000
rg.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.google.common.collect.Sets;
import com.whggbd.bsz.entity.TSysMenu;
import com.whggbd.bsz.entity.TSysUser;
import com.whggbd.bsz.service.UserMapperService;

@Service("userDetailsService")
public class ETUserDetailService implements UserDetailsService {

@Autowired
private UserMapperService userMapperService;

@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
TSysUser sysUser = userMapperService.getUserByUserName(username);
if (sysUser == null) {
System.out.println("username is " + username);
throw new UsernameNotFoundException(username);

}
Collection<GrantedAuthority> auths = Sets.newHashSet();
List<TSysMenu> sysMenu =userMapperService.getMenusByUserId(new Long((long)sysUser
.getUSER_ID()));
for (TSysMenu items : sysMenu) {
auths.add(new SimpleGrantedAuthority(items.getDISPLAY_NAME()));
}

return new UserDetail(sysUser, true,true, true, true, auths);
}

}

4.InvocationSecurityMetadataSourceServiceImpl
package com.whggbd.bsz.secutiry;

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 java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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 org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;

import com.google.gson.Gson;
import com.whggbd.bsz.entity.TSysMenu;
import com.whggbd.bsz.entity.TSysRole;
import com.whggbd.bsz.entity.TSysUser;
import com.whggbd.bsz.service.UserMapperService;

@Service("mySecurityMetadataSource")
public class InvocationSecurityMetadataSourceServiceImpl implements
FilterInvocationSecurityMetadataSource {

private List<TSysMenu> sysMenus;

@Autowired
private UserMapperService userMapperService;

private static Map<String, Collection<ConfigAttribute>> resourceMap = new ConcurrentHashMap<String, Collection<ConfigAttribute>>();
//private AntPathMatcher urlMatcher = new AntPathMatcher();

public InvocationSecurityMetadataSourceServiceImpl() {

super();

sysMenus = new ArrayList<TSysMenu>();
}

@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}

private void loadResourcesDefine() {

System.out.println("MySecurityMetadataSource.loadResourcesDefine()--------------开始加载资源列表数据--------");

if (sysMenus.size() == 0)
{
sysMenus = userMapperService.getMenus();
}
for (TSysMenu sysMenu : sysMenus) {
// Collection<ConfigAttribute> configAttributes = null;
String url = sysMenu.getURL();
Collection<ConfigAttribute> value = null;
ConfigAttribute configAttribute = new SecurityConfig(
sysMenu.getDISPLAY_NAME());
if (resourceMap.containsKey(url)) {
value = resourceMap.get(url);
value.add(configAttribute);
resourceMap.put(url, value);
} else {
value = new ArrayList<ConfigAttribute>();
value.add(configAttribute);
resourceMap.put(url, value);
}
}
//      System.out.println("权限有:" + new Gson().toJson(resourceMap));

}

/*
* 根据请求的资源地址,获取它所拥有的权限
*/
@Override
public Collection<ConfigAttribute> getAttributes(Object obj)
throws IllegalArgumentException {
// object 是一个URL,被用户请求的url。
this.loadResourcesDefine();
String url = ((FilterInvocation) obj).getRequestUrl();
System.out.println("MySecurityMetadataSource:getAttributes()---------------请求地址为:"
+ url);
//        Pattern p = Pattern.compile("/views");
//        Matcher m = p.matcher(url);
//        if(m.find()){
// // ?所在的索引位
// int firstQuestionMarkIndex = url.indexOf("?");
// //存在就截取
// if (firstQuestionMarkIndex != -1)
// {
// url = url.substring(0, firstQuestionMarkIndex);
// }
// url集合
Iterator<String> ite = resourceMap.keySet().iterator();

while (ite.hasNext()) {
String resURL = ite.next();

if (url.indexOf(resURL) != -1) {
System.out.println("所需权限是:"
+ new Gson().toJson(resourceMap.get(resURL)));
return resourceMap.get(resURL);
}
}
//        }
return null;

}

@Override
public boolean supports(Class<?> arg0) {
System.out
.println("MySecurityMetadataSource.supports()---------------------");
return true;
}

}

5.MyFilterSecurityInterceptor.class
package com.whggbd.bsz.secutiry;

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;

public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter
{
//与applicationContext-security.xml里的myFilter的属性securityMetadataSource对应,
//其他的两个组件,已经在AbstractSecurityInterceptor定义
private FilterInvocationSecurityMetadataSource securityMetadataSource;

@Override
public SecurityMetadataSource obtainSecurityMetadataSource()
{
return this.securityMetadataSource;
}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}

private void invoke(FilterInvocation fi) throws IOException, ServletException
{
// object为FilterInvocation对象
//super.beforeInvocation(fi);源码
//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);
}
}

public FilterInvocationSecurityMetadataSource getSecurityMetadataSource()
{
return securityMetadataSource;
}

public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource)
{
this.securityMetadataSource = securityMetadataSource;
}

public void init(FilterConfig arg0) throws ServletException
{
// TODO Auto-generated method stub
}

public void destroy()
{
// TODO Auto-generated method stub

}

@Override
public Class<? extends Object> getSecureObjectClass()
{
//下面的MyAccessDecisionManager的supports方面必须放回true,否则会提醒类型错误
return FilterInvocation.class;
}
}

6.MyPasswordEncoder.class

package com.whggbd.bsz.secutiry;

import org.springframework.security.authentication.encoding.PasswordEncoder;

/**
*
*/
@SuppressWarnings("deprecation")
public class MyPasswordEncoder implements PasswordEncoder
{

@Override
public String encodePassword(String pwd, Object arg1)
{
return pwd;
}

@Override
public boolean isPasswordValid(String npwd, String opwd, Object arg2)
{
System.out.println("MyPasswordEncoder.isPasswordValid()...................");
return npwd.equals(opwd);
}

}

7.MyAccessDecisionManager.class
package com.whggbd.bsz.secutiry;

import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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;
import org.springframework.security.web.access.AccessDeniedHandler;

public class MyAccessDecisionManager implements AccessDecisionManager {
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {

System.out
.println("MyAccessDecisionManager.decide()..................");
// 如果权限列表为空
if (null == configAttributes)
return;
// 权限列表
Iterator<ConfigAttribute> it = configAttributes.iterator();
while (it.hasNext()) {
ConfigAttribute ca = it.next(); // 权限
// 相应的权限
String needRole = ((SecurityConfig) ca).getAttribute();

// ga 为用户所被赋予的权限
// needRole 为访问相应的资源应该具有的权限。
for (GrantedAuthority ga : authentication.getAuthorities()) {
if (needRole.trim().equals(ga.getAuthority().trim())) {
return;
}
}
}
throw new AccessDeniedException("无权访问该资源!!!");
}

public boolean supports(ConfigAttribute attribute) {
return true;
}

public boolean supports(Class<?> clazz) {
return true;
}

}
8.自定义 MySimpleUrlAuthenticationSuccessHandler.class
public class MySimpleUrlAuthenticationSuccessHandler extends
SimpleUrlAuthenticationSuccessHandler {
......
}
9.自定义MySimpleUrlAuthenticationFailureHandler.class
public class MySimpleUrlAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
......
}
[code]参考博客:https://www.cnblogs.com/xiongyu/archive/2012/07/21/2602220.html
[code]如有不足请指正!

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: