spring-security

个人代码地址:https://github.com/zfgod/securityDemo,期待共同学习进步

1.项目环境及说明:

jdk1.7+tomcat7+IDEA14+Maven3.1+Mysql5.6

H5+AngularJs1+SpringMvc+SpringSecurity+Spring4.2.3RELEASE+Mybatis3.1

2.Security加入到项目环境:

2.1 依赖添加:

<spring.security.version>3.2.3.RELEASE</spring.security.version>
<!-- spring security start-->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>${spring.security.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>${spring.security.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>${spring.security.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>${spring.security.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-acl</artifactId>
    <version>${spring.security.version}</version>
</dependency>
<!-- spring security end -->

2.2 数据库建立权限表及相应实体相关:

用户user - 角色 role - 权限资源 resource

2.3 security权限设置

spring主配置文件引入spring-security.xml:

  <?xml version="1.0" encoding="UTF-8"?>
<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-4.2.xsd
	http://www.springframework.org/schema/security 
	http://www.springframework.org/schema/security/spring-security-3.2.xsd">

	<!-- 资源后缀匹配放行 -->
	<http pattern="/*.html"  security="none" /><!-- 例:webapp下面index.html -->
	<http pattern="/**/*.html"  security="none" />
	<http pattern="/**/*.js"   security="none" />
	<http pattern="/**/*.css"  security="none" />
	<http pattern="/**/*.png"  security="none" />
	<!-- 这里采用静态页面+js请求数据,导航由前端做控制, 后台直接放行
	     注意:静态资源的放行,多层目录pattern匹配是/**/*.js 而不是/*.js 或者*.js  
                       多层目录/**
	-->
	<!-- 一些没必要加入权限资源的接口,给予放行-->
	<http pattern="/*/letGo/*" security="none"/>
	<!-- 登录相关 -->
    <http pattern="/sys/commitLogin.do" security="none" />
    <http pattern="/sys/loginOut.do" security="none" />

	<http use-expressions="true" auto-config="false"
		  entry-point-ref="myAuthenticationEntryPoint"
		  access-denied-page="/denied.html">
		<!-- access-denied-page 只对非js请求的拒绝可以跳转,
		     如果是js请求被拒:response会是拒绝页面的html代码块
		     -->
        <intercept-url pattern="/*" access="isAuthenticated()" />
		<!-- 路径: resource.security.MySecurityFilter -->
        <custom-filter ref="mySecurityFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
	</http>
	<!-- 未登录的切入点 -->
    <beans:bean id="myAuthenticationEntryPoint"
				class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <beans:property name="loginFormUrl" value="/pages/sys/login.html"></beans:property>
    </beans:bean>
      
	<authentication-manager alias="myAuthenticationManager">
		<!-- 路径:resource.security.MyUserDetailServiceImpl-->
		<authentication-provider user-service-ref="myUserDetailServiceImpl" />
	</authentication-manager>
</beans:beans>

重新定义Filter细节:

@Service("mySecurityFilter")
public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter {
	//与spring-security.xml里的myFilter的属性securityMetadataSource对应,
	//其他的两个组件,已经在AbstractSecurityInterceptor定义
	@Autowired
	private MySecurityMetadataSource securityMetadataSource;
	@Autowired
	private MyAccessDecisionManager accessDecisionManager;
	@Autowired
	private AuthenticationManager myAuthenticationManager; 
	
	@PostConstruct
	public void init(){
		super.setAuthenticationManager(myAuthenticationManager);
		super.setAccessDecisionManager(accessDecisionManager);
	}
	
	@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 {
		InterceptorStatusToken token = super.beforeInvocation(fi);
		try {
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		} finally {
			super.afterInvocation(token, null);
		}
	}

	public void init(FilterConfig arg0) throws ServletException {
	}
	
	public void destroy() {
		
	}

	@Override
	public Class<? extends Object> getSecureObjectClass() {
		return FilterInvocation.class;
	}
}

a.自定义系统所有权限资源加载和资源路径权限匹配

/**
 * 加载资源与权限的对应关系
 * */
@Service
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
	@Autowired
	private ResourcesMapper resourcesMapper;

	private static Map<String, Collection<ConfigAttribute>> resourceMap = null;

	public Collection<ConfigAttribute> getAllConfigAttributes() {

		return null;
	}

	public boolean supports(Class<?> clazz) {
		return true;
	}
	/**
	 * 加载所有资源与权限的关系
	 */
	@PostConstruct
	private void loadResourceDefine() {
//		System.err.println("-----------MySecurityMetadataSource loadResourceDefine ----------- ");
		if (resourceMap == null) {
			resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
			List<Resources> resources = this.resourcesMapper.findAll();
			ConfigAttribute configAttribute;
			String resUrl;
			for (Resources resource : resources) {
				//通过资源名称来表示具体的权限 注意:必须"ROLE_"开头
				configAttribute = new SecurityConfig("ROLE_" + resource.getResKey());
				resUrl = resource.getResUrl();
				if(resourceMap.containsKey(resUrl)){
					resourceMap.get(resUrl).add(configAttribute);
				}else {
                    //新put key-value,value list必须新建
					Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
					configAttributes.add(configAttribute);
					resourceMap.put(resource.getResUrl(), configAttributes);
				}
			}
		}
	}
	/**
	 * 	返回所请求资源路径 所需要的权限
	 */
	public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
		String requestUrl = ((FilterInvocation) object).getRequestUrl();
		if(resourceMap == null) {
			loadResourceDefine();
		}
		if(requestUrl.indexOf("?")> -1){//处理请求地址后面带参数
			requestUrl=requestUrl.substring(0,requestUrl.indexOf("?"));
		}
		Collection<ConfigAttribute> configAttributes = resourceMap.get(requestUrl);
            /*如果为null,视为系统未定义的资源路径*/
		if(configAttributes == null){
			configAttributes = resourceMap.get("undefine");//此权限每个用户都不具有,则未加入的url不会通过
		}
		return configAttributes;
	}
}

b.自定义用户登录,加载用户认证资源

in action/controller:

       if(loginFlag){//登录成功
           //进入用户认证
           //session存储      
           Authentication authentication = myAuthenticationManager
                   .authenticate(
                           new UsernamePasswordAuthenticationToken(hasUser.getUserName(),hasUser.getUserPassword()));
           SecurityContext securityContext = SecurityContextHolder.getContext();
           securityContext.setAuthentication(authentication);
           session.setAttribute(ParamsUtils.user_security_sedssin, securityContext);
                   // 当验证都通过后,把用户信息放在session里
           session.setAttribute(ParamsUtils.user_sessin, hasUser);  
       }

调用myAuthenticationManager.authenticate()进入自定义的用户权限加载:


@Service
public class MyUserDetailServiceImpl implements UserDetailsService {
	
	@Autowired
	private UserMapper userDao;
	@Autowired
	private ResourcesMapper resourcesDao ;
	// 登录成功 加载用户的资源权限
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// 取得用户的权限
		sys.model.User users = userDao.querySingleUser(username);
		if  (users == null)
            throw new UsernameNotFoundException(username+" not exist!");  
		Collection<GrantedAuthority> grantedAuths = obtionGrantedAuthorities(users);
		// 封装成spring security的user
        // org.springframework.security.core.userdetails.User 
		User userDetail = new User(
				users.getUserName(), 
				users.getUserPassword(),
				true, true, true,true, 
				grantedAuths	//用户的权限
			);
		return userDetail;
	}

	// 取得用户的权限
	private Set<GrantedAuthority> obtionGrantedAuthorities(sys.model.User user) {
		List<Resources> resources = resourcesDao.getUserResources(String.valueOf(user.getUserName()));
		Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
		for (Resources res : resources) {
			// (或者说用户所拥有的权限) 注意:必须"ROLE_"开头
			authSet.add(new SimpleGrantedAuthority("ROLE_" + res.getResKey()));
		}
		return authSet;
	}
}

c.自定义认证过程

    请求时会先进入MySecurityMetadataSource.getAttributes() 进行路径资源匹配,获取所需权限后进入认证管理器进行认证: 

@Service
public class MyAccessDecisionManager implements AccessDecisionManager {
	public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException, InsufficientAuthenticationException {
		if(configAttributes == null) {
			return;
		}
		/*所请求的资源拥有的权限(一个资源对多个权限)*/
		Iterator<ConfigAttribute> iterator = configAttributes.iterator();
		/*当前用户的资源权限*/
		Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
		/*多个权限,这里采用只要用户具备其中一个就表示可以通过*/
		ConfigAttribute configAttribute;
		String needPermission;
		while(iterator.hasNext()) {
			configAttribute = iterator.next();
			//访问请求资源所需要的权限
			needPermission = configAttribute.getAttribute();
			//用户所拥有的权限authentication
			for(GrantedAuthority ga : authorities) {
				if(needPermission.equals(ga.getAuthority())) {
					return;
				}
			}
		}
		throw new AccessDeniedException(" 没有权限访问! ");
	}

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

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

2.4 项目security运行流程:

a. 系统启动,装载整个系统配置的权限资源

b. 自定义用户登录,登录成功调用 security: authenticate(userName,password)

进入自定义的登录用户的权限资源装载:将会获取用户持有的权限资源,转为SecurityContext,存入session

c. 用户访问资源及操作:security 在action之前进去拦截,根据url获取所需权限信息并进入自定义的认证

转载于:https://my.oschina.net/u/2471663/blog/807164

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值