系统开发中的用户权限的两种流行实现。

用户权限,url拦截,这在系统开发是不可少的,也是很重要的一环。有时候,我们可能在页面隐藏掉某些功能,但是,一旦我们直接通过url访问,或者自己写代码去访问的时候,如果没有权限的控制,那么我们的私有数据将暴露。知道了其重要性,那么该怎么去实现一个管理url方便,且安全的权限系统呢。

其实现在市场上提供了很多第三方的安全框架,例如spring security,shrio。这些在工作中都是使用非常普遍的,又或者自己在spring mvc或者Struct框架中,使用拦截器去实现。又或者最原始的servlet中的拦截器。实现的方式有很多种,但是原理都是一样的。

而且最最常见的无非得经过三个过滤:

1.访问的url是否合法(带参的检查)

2.是否登录

3.是否对当前url拥有权限

第一步可要可不要。但是是否登录,是否对当前url拥有权限,则是必要的。

一般普遍且简单的权限系统,表设计:

1.用户表

2.角色表

3.url资源表

4.用户---角色表   (多对多)

5.角色---url资源表   (多对多)


现在不谈实现,先谈原理,走一遍流程:

用户登录------根据输入用户id拿用户整一条数据------如果拿不到,那么返回错误说该用户不存在,如果拿到了用户数据,比对输入的密码,比对是否过期等用户自定义的条件,比对正确,那么成功登录。若不正确,则返回错误的信息。若正确后,那么可以在request中拿到用户登录的日志,写进数据库。将用户数据存储到session中(或者自己使用redis搭建的缓存,这其实类似session)。成功登录以后,将会立刻跳转到主页url。此时此请求将要走权限系统了。

首先得判断是否登录了,如果session中有这个用户的数据,那么证明登录了,走过登录验证后,将走权限验证:根据url去找角色,根据用户去找角色,如果两者有共同角色,那么证明该用户拥有该url的权限。

整个流程就是这样了。

如何用代码去实现呢?

在spring mvc框架下,我总结两种流行的实现方式:

自己使用spring mvc提供的拦截器去实现:

首先springmvc配置文件配置:

<!-- 登录拦截 ,权限拦截-->
<mvc:interceptors>
		<mvc:interceptor>
			<mvc:mapping path="/androidpublic/*.do"/>
			<mvc:mapping path="/Ad**/**/*.do"/>
			<mvc:exclude-mapping path="/androidpublic/alogin.do"/>
			<mvc:exclude-mapping path="/androidpublic/logout.do"/>
			<bean class="com.app.zf.itsm.android.publics.interceptor.AdLoginHandlerIntercep"/>
		</mvc:interceptor>
	</mvc:interceptors>
这个配置的意思是:对/androidpublic/*.do          /Ad**/**/*.do的请求进行拦截,对/androidpublic/alogin.do          /androidpublic/logout.do忽略掉。

然后拦截器是:

com.app.zf.itsm.android.publics.interceptor.AdLoginHandlerIntercep

这是我一个项目里安卓服务端的拦截器:

public class AdLoginHandlerIntercep extends HandlerInterceptorAdapter{

	
	/**autor:cwy
	 * 00表示不是最初来源,01代表尚未登录。将登陆验证和url权限验证写在一起
	 * 说明:首先验证登录,在登录的基础上验证来源是否正确,最后验证是否有权限
	 * 权限验证这里有三种情况是不拦截的:1.url池里一个url都没有,2.url没有录入进资源管理或者,url没有一个宿主,3。url录入,且有宿主,但是另外的宿主没有分配权限
	 * 注意事项:一旦一个url录入进资源管理,必须先给他一个宿主(系统管理员),否则,所有人都可以访问这个url
	 */
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		PageData pd = new PageData();
		pd=new PageData(request);
		String token1=request.getParameter("TOKEN1");
		String orgin=request.getParameter("ORIGIN");
		String path=request.getRequestURI();
			if(StringUtils.isNotEmpty(RedisPool.get(token1)))              
			{
				
				if(StringUtils.isNotEmpty(request.getParameter("INFOR")))     //已登录,还需要验证签名(签名存在)
				{
					String token2=RedisPool.get(token1);       //获得真正的token
					String origin = MD5.md5(path+"?"+token2+orgin);   //获取token和url的MD5加密
					if(!origin.equals(pd.get("INFOR")))                    //对比签名,防止url被人拦截后,模拟发送请求
					{
						response.getWriter().print(InterceptorStatusConstant.NO_ORGIN);                                //不是最初来源
						return false;
					}
					else
					{
						//已登录,签名通过,还需要验证是否对url有访问权限
						String url=request.getServletPath();
						//找到所有的url
						List<String> allUrlList = MemeryCacheManager.getResourceRoleCache().getList("ALL_URL_LIST", String.class); // 从缓存中读取系统中所有url集合,进行对比
						if (CollectionUtils.isEmpty(allUrlList)) {
							SysResourceService resourceService = (SysResourceService) SpringContextUtil.getBean("sysresourceservice");
							allUrlList = resourceService.findAllUrl();
						}
						
						if (CollectionUtils.isNotEmpty(allUrlList)) {            //若数据库存在url,且找不到url与请求的url相同,那么将不拦截;如果有相同,在验证是否url对应的角色与用户拥有的角色是否相同,相同则不拦截,不相同则拦截,如果对应的url对应的角色为null,则不拦截。
							for (String resURL : allUrlList) {
								if (StringUtils.startsWith(url, resURL)) {
									List<ConfigAttribute> atts = MemeryCacheManager.getResourceRoleCache().getList(resURL, ConfigAttribute.class); // 从缓存读取对应请求url,所包含的角色集合
									if (CollectionUtils.isEmpty(atts)) {
										SysResourceService resourceService = (SysResourceService) SpringContextUtil.getBean("sysresourceservice");
										atts = resourceService.findConfigAttByURL(resURL);           //通过url找url对应的角色,若找到了,执行下一步,找不到的话,代表url没有宿主,所以这个url可以被所有的用户访问
									}

									if (CollectionUtils.isNotEmpty(atts)) {
										Iterator<ConfigAttribute> ite = atts.iterator(); // 请求URL所对应的角色集合
										while (ite.hasNext()) {
											ConfigAttribute ca = ite.next();
											String needRole = ((SecurityConfig) ca).getAttribute();
											//从redis缓存中拿用户角色代码字符串,并转化成数组
											 JSONArray relecodesJson=JSON.parseArray(RedisPublicInforUtil.getRolesCodes(request));
											 Object rolecodes[]= relecodesJson.toArray();
											for (int i=0;i<rolecodes.length;i++) { // 用户所拥有角色
												if (StringUtils.equals(StringUtils.trim(needRole), StringUtils.trim(rolecodes[i].toString()))) {
													return true;
												}
											}
										}
											response.getWriter().print(InterceptorStatusConstant.NO_PERMISION);                  //无对URL权限
											return false;
									} else {
										return true;               //url没有宿主(都可以访问)
									}
								}
							}
							return true;                          //url没配进url池
						}
						else
						{
							return true;                 //url池里一个url都没有
						}
						
						
					} 
				}
				else
				{
				response.getWriter().print(InterceptorStatusConstant.NO_ORGIN);
				return false;                                                      //请求为没有带标记
				}
			}
			else
			{
				response.getWriter().print(InterceptorStatusConstant.NO_LOGIN);                                 //没有登录
				return false;
			}
	}
}

第二种实现:使用spring security来实现

spring security 在市场上还是很受欢迎的。它的功能很强大,不仅仅是拦截,还提供单点登录,即后一个用户登录会挤掉前一个登录的用户(这是可配置的)。

来自当前正在开发的系统的一个spring security配置文件:

<?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-3.0.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-3.1.xsd">

	<!-- 不要拦截图片等静态资源,其中**代表可以跨越目录,*不可以跨越目录。  security=none可以理解为忽略-->
	<!--<http pattern="/login_ace.jsp*" security="none"/>-->
	<http pattern="/yinlinlogin.jsp*" security="none"/>
	<http pattern="/common/**" security="none"/>
	<!-- 不要过滤图片等静态资源,安卓的url不让spring security来管理 --> 
	<http pattern="/**/*.jpg" security="none"/>
  	<http pattern="/**/*.png" security="none"/>
  	<http pattern="/**/*.gif" security="none"/>
  	<http pattern="/**/*.css" security="none"/>
  	<http pattern="/**/*.js" security="none"/> 
  	<http pattern="/**/*.swf" security="none"/> 
	<http pattern="/j_captcha.do*" security="none"/>
	<http pattern="/androidpublic/*.do" security="none"/>
	<http pattern="/Adcmdb/**/*.do" security="none"/>
	
	
	
	<!-- auto-config = true 则使用from-login. 如果不使用该属性 则默认为http-basic(没有session). 
		access-denied-page:出错后跳转到的错误页面; -->
	<http auto-config="true" access-denied-page="/common/403_denied.jsp">
	
		<!-- login-page:默认指定的登录页面. authentication-failure-url:验证失败跳转页面. default-target-url:成功登陆后跳转页面 -->
		<form-login login-page="/yinlinlogin.jsp?login" authentication-failure-url="/yinlinlogin.jsp?failure" default-target-url="/index/yinlinindex.do" />
		
		<!-- logout-success-url:成功注销后跳转到的页面; -->
		<logout logout-success-url="/yinlinlogin.jsp?logout" />
		
		<http-basic />

		<!-- session-management是针对session的管理. 这里可以不配置. 如有需求可以配置. -->
		<!-- id登陆唯一. 后登陆的账号会挤掉第一次登陆的账号 error-if-maximum-exceeded="true" 禁止2次登陆; 
			session-fixation-protection="none" 防止伪造sessionid攻击. 用户登录成功后会销毁用户当前的session. 
			创建新的session,并把用户信息复制到新session中. expired-url : session过期跳转 -->
		<session-management invalid-session-url="/yinlinlogin.jsp?invalid">
			<concurrency-control expired-url="/yinlinlogin.jsp?expired" />
		</session-management>

		<!-- 增加一个filter,不能修改默认的filter了,这个filter位于FILTER_SECURITY_INTERCEPTOR之前 -->
		<custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="exFilter" />
	</http>
	
	<!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,我们的所有控制将在这三个类中实现,解释详见具体配置 -->
	<beans:bean id="exFilter" class="com.app.core.security3.MyFilterSecurityInterceptor">
		<beans:property name="authenticationManager" ref="authenticationManager" />
		<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
		<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
	</beans:bean>

	<!-- 权限管理操作 -->
	<authentication-manager alias="authenticationManager">
		<authentication-provider user-service-ref="userDetailService">
			<password-encoder hash="md5"/>
		</authentication-provider>
	</authentication-manager>

	<!-- 用户信息 -->
	<beans:bean id="userDetailService" class="com.app.core.security3.MyUserDetailService">
		<beans:property name="sysUserService" ref="sysuserservice"/>
		<beans:property name="sysOrganizationService" ref="sysorganizationservice"/>
		<beans:property name="sysUserLoginLogService" ref="sysUserLoginLogService"/>
	</beans:bean>
	
	<!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
	<beans:bean id="accessDecisionManager" class="com.app.core.security3.MyAccessDecisionManager" />
	
	<!-- 资源源数据定义,即定义某一资源可以被哪些角色访问 -->
	<beans:bean id="securityMetadataSource" class="com.app.core.security3.MyInvocationSecurityMetadataSource" />

	<!-- 国际化 -->
	<beans:bean id="messageSource"
		class="org.springframework.context.support.ResourceBundleMessageSource">
		<beans:property name="basenames">
			<beans:list>
				<beans:value>org.springframework.security.messages</beans:value>
			</beans:list>
		</beans:property>
	</beans:bean>
	
</beans:beans>

对于登录,spring security也帮我们做了,验证码是用了谷歌的

 <br><div class="login_title">系统登录</div>
            <div class="login_list" title="Register">
                <form id="loginForm" action="${pageContext.request.contextPath}/j_spring_security_check" method="post">
                <!-- 账号 -->
                <div class="login_list_li">
                    <div class="login_list_li_icon icon35 icon35_01"></div>
                    <input  class="easyui-textbox" id="j_username" name="j_username" data-options="prompt:'     输入账号...'">
                </div>
                <!-- 密码 -->
                <div class="login_list_li">
                    <div class="login_list_li_icon icon35 icon35_02"></div>
                    <input name="j_password" type="password"  class="easyui-textbox" data-options="prompt:'     输入密码...'">
                	
                	<i class="ace-icon fa fa-user"></i>
                </div>
                <!-- 验证码 -->
                <div class="login_list_li login_list_li_min clearfix">
                    <div class="login_list_li_icon icon35 icon35_03"></div>
                    <input  id="j_kaptcha" name="j_kaptcha" class="easyui-textbox" data-options="prompt:'     验证码...'">            							
                	<div class="Fl login_list_li_Msg">
						<label class="inline">
							<img src="${pageContext.request.contextPath}/j_captcha.do" style="cursor: pointer;width: 75px;height: 24px;" id="kaptchaImage" title="重新获取" οnclick="reloadKaptcha()"/>
						</label>
			  		</div>
                </div>
               <font color="red">${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}</font>
                <!--  <div id="showMsg"></div>-->
                <!-- 登录按钮 -->
                <input type="submit" class="easyui-linkbutton login_ok"  style="width:100%;height:32px" value="登  陆"/>
            </form>
            </div>


验证码:

<!-- Kaptcha验证码 -->
	<bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
		<property name="config">
			<bean class="com.google.code.kaptcha.util.Config">
				<constructor-arg>
					<props>
						<prop key="kaptcha.border">yes</prop>
						<prop key="kaptcha.border.color">211,211,211</prop>
						<prop key="kaptcha.textproducer.font.color">47,79,79</prop>
						<prop key="kaptcha.textproducer.font.size">23</prop>
						<prop key="kaptcha.image.width">75</prop>
						<prop key="kaptcha.image.height">24</prop>
						<prop key="kaptcha.textproducer.char.string">1234567890</prop>
						<prop key="kaptcha.textproducer.char.length">4</prop>
						<prop key="kaptcha.textproducer.font.names">新宋体</prop>
						<prop key="kaptcha.background.clear.from">white</prop>
						<prop key="kaptcha.noise.impl">com.google.code.kaptcha.impl.NoNoise</prop>
						<prop key="kaptcha.obscurificator.impl">com.google.code.kaptcha.impl.ShadowGimpy</prop>
					</props>
				</constructor-arg>
			</bean>
		</property>
	</bean>

点击登录提交:会走spring security框架:到达:UserDetailsService;一般情况下,我们需要实现它,重写

@Override
	public UserDetails loadUserByUsername(String arg0)
			throws UsernameNotFoundException {
		// TODO Auto-generated method stub
		return null;
	}
arg0代表的其实就是账号。这个方法里面需要放置的逻辑是:根据账号拿用户信息,包括基本信息和额外需要用到的信息,例如用户拥有的角色,用户所在的数据域等等,然后将其返回。框架会自动帮助我们去判断密码,账号是否到期等等。

一个实现实例:

用户实体类需要实现框架的UserDetails接口:这个接口是

public class SysUser extends BaseEntity implements UserDetails, Serializable {

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getPassword() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getUsername() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		return false;
	}
}

一个实现它的实例:

public class SysUser extends BaseEntity implements UserDetails, Serializable {
	private static final String mgrAccount = "admin";//系统管理员
	
	private static final long serialVersionUID = 1L;
	private String username; // 名称
	private String sex; // 性别
	private String loginId; // 登陆账号
	private String password; // 密码
	private String enName; // 英文名
	private String employeeId; // 员工ID
	private String post; // 岗位
	private String duty; // 职务
	private String email; // 邮箱
	private String qq;
	private String mobile; // 手机
	private String officePhone; // 办公电话
	private String dutyDesc; // 职责
	private String headImage; // 头像地址
	private Date birthday; // 生日
	private Date joinDate; // 入职日期
	private Date expiredDate; // 合同到期日期
	private String stateFlag; // 状态 1:在职,2:离职,3:待定
	private String showState; // 隐藏状态 y:显示,h:隐藏
	private String deleteFlag; // n:未删除
	private Integer orderBy; // 排序
	private String remark; // 备注
	private String companyId; // 公司ID
	

	private String phonecode; //手机唯一码

	

	@Transient
	public String getPhonecode() {
		return phonecode;
	}

	@Transient
	public void setPhonecode(String phonecode) {
		this.phonecode = phonecode;
	}

	// 临时
	private String birthdayFmt; // 格式化
	private String joinDateFmt; // 格式化
	private String expiredDateFmt; // 格式化
	private String expiredState; // 过期状态
	private String companyName; // 公司名称
	private String companyCode; // 公司编码
	private String companyType; // 公司类型

	private String orgId; // 组织ID
	private String orgName; // 组织名称
	private String parentOrgIds; // 父辈组织ID
	private String parentOrgNames; // 父辈组织
	private String virtualOrgId; // 虚拟组织ID
	private String virtualOrgName; // 虚拟组织名称
	private String postName; // 岗位名称
	private String dutyName; // 职务名称
	private String roleId; // 角色ID
	private boolean isManage; // 是否系统管理机构,属于该组织下的人员拥有查看全部数据权限
	private List<SysRole> roles; // 拥有的角色集合
	private List<SimpleGrantedAuthority> authlist = new ArrayList<SimpleGrantedAuthority>(); // spring_security验证角色编码
	List<SysCompany> companyList; // 关联公司或服务商
	List<SysUserGroup> userGroupList; // 所属班组
	private Date dutywatchadate; //排班的值班日期

	public SysUser() {
	}

	public SysUser(String resourceid, String username) {
		super();
		setResourceid(resourceid);
		this.username = username;
	}

	@Column(name = "USERNAME", length = 50)
	public String getUsername() {
		return this.username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@Column(name = "SEX", length = 50)
	public String getSex() {
		return this.sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	@Column(name = "LOGIN_ID", length = 50)
	public String getLoginId() {
		return this.loginId;
	}

	public void setLoginId(String loginId) {
		this.loginId = loginId;
	}

	@Column(name = "PASSWORD", length = 32)
	public String getPassword() {
		return this.password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Column(name = "EN_NAME", length = 50)
	public String getEnName() {
		return this.enName;
	}

	public void setEnName(String enName) {
		this.enName = enName;
	}

	@Column(name = "EMPLOYEE_ID", length = 50)
	public String getEmployeeId() {
		return this.employeeId;
	}

	public void setEmployeeId(String employeeId) {
		this.employeeId = employeeId;
	}

	@Column(name = "POST", length = 50)
	public String getPost() {
		return this.post;
	}

	public void setPost(String post) {
		this.post = post;
	}

	@Column(name = "DUTY", length = 50)
	public String getDuty() {
		return duty;
	}

	public void setDuty(String duty) {
		this.duty = duty;
	}

	@Column(name = "EMAIL", length = 100)
	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	@Column(name = "QQ", length = 50)
	public String getQq() {
		return this.qq;
	}

	public void setQq(String qq) {
		this.qq = qq;
	}

	@Column(name = "MOBILE", length = 100)
	public String getMobile() {
		return this.mobile;
	}

	public void setMobile(String mobile) {
		this.mobile = mobile;
	}

	@Column(name = "OFFICE_PHONE", length = 100)
	public String getOfficePhone() {
		return this.officePhone;
	}

	public void setOfficePhone(String officePhone) {
		this.officePhone = officePhone;
	}

	@Column(name = "DUTY_DESC", length = 650)
	public String getDutyDesc() {
		return this.dutyDesc;
	}

	public void setDutyDesc(String dutyDesc) {
		this.dutyDesc = dutyDesc;
	}

	@Column(name = "HEAD_IMAGE", length = 150)
	public String getHeadImage() {
		return this.headImage;
	}

	public void setHeadImage(String headImage) {
		this.headImage = headImage;
	}

	@Temporal(TemporalType.DATE)
	@Column(name = "BIRTHDAY")
	public Date getBirthday() {
		return this.birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;

		birthdayFmt = birthday != null ? DateTimeUtil.formatDate(birthday) : "";
	}

	@Temporal(TemporalType.DATE)
	@Column(name = "JOIN_DATE")
	public Date getJoinDate() {
		return this.joinDate;
	}

	public void setJoinDate(Date joinDate) {
		this.joinDate = joinDate;

		joinDateFmt = joinDate != null ? DateTimeUtil.formatDate(joinDate) : "";
	}

	@Temporal(TemporalType.DATE)
	@Column(name = "EXPIRED_DATE")
	public Date getExpiredDate() {
		return expiredDate;
	}

	private boolean expiredBool = true; // 过期标志:默认有效

	public void setExpiredDate(Date expiredDate) {
		this.expiredDate = expiredDate;

		expiredDateFmt = expiredDate != null ? DateTimeUtil.formatDate(expiredDate) : "";

		if (null == expiredDate) {
			setExpiredState("长期");
		} else {
			if (DateTimeUtil.addDays(expiredDate, 1).compareTo(new Date()) < 0) { // 合同到期时间小与当前时间,账号过期
				setExpiredState("<font color='red'>过期</font>");
				expiredBool = false;
			} else {
				setExpiredState("有效");
			}
		}
	}

	@Column(name = "STATE_FLAG", length = 1)
	public String getStateFlag() {
		return stateFlag;
	}

	public void setStateFlag(String stateFlag) {
		this.stateFlag = stateFlag;
	}

	@Column(name = "SHOW_STATE", length = 1)
	public String getShowState() {
		return showState;
	}

	public void setShowState(String showState) {
		this.showState = showState;
	}

	@Column(name = "DELETE_FLAG", length = 1)
	public String getDeleteFlag() {
		return deleteFlag;
	}

	public void setDeleteFlag(String deleteFlag) {
		this.deleteFlag = deleteFlag;
	}

	@Column(name = "ORDER_BY")
	public Integer getOrderBy() {
		return this.orderBy;
	}

	public void setOrderBy(Integer orderBy) {
		this.orderBy = orderBy;
	}

	@Column(name = "REMARK", length = 500)
	public String getRemark() {
		return this.remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}

	@Column(name = "COMPANY_ID")
	public String getCompanyId() {
		return companyId;
	}

	public void setCompanyId(String companyId) {
		this.companyId = companyId;
	}

	/**
	 * 是否系统管理机构,属于该组织下的人员拥有查看全部数据权限
	 * 
	 * @return true:是(查看全部数据)、false:否(只能查看本公司数据)
	 */
	@Transient
	public boolean isManage() {
		if(isManage && mgrAccount.equals(this.getLoginId())){
			return true;
		}else{
			return false;
		}
	}

	public void setManage(boolean isManage) {
		this.isManage = isManage;
	}

	@Transient
	public List<SysRole> getRoles() {
		return roles;
	}

	public void setRoles(List<SysRole> roles) {
		this.roles = roles;

		// 组装用户拥有角色成spring security验证
		if (roles != null && roles.size() > 0) {
			for (SysRole role : roles) {
				authlist.add(new SimpleGrantedAuthority(role.getRoleCode()));
			}
		}
	}

	/**
	 * 过期标志
	 */
	@Transient
	public boolean isAccountNonExpired() {
		return expiredBool;
	}

	/**
	 * 锁定标志,在职状态才能进入系统
	 */
	@Transient
	public boolean isAccountNonLocked() {
		return StringUtils.equalsIgnoreCase(stateFlag, "1") ? true : false;
	}

	@Transient
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Transient
	public boolean isEnabled() {
		return true;
	}

	// 重写equals与hashCode,判断用户相等
	public boolean equals(Object rhs) {
		if (!(rhs instanceof SysUser) || (rhs == null)) {
			return false;
		}
		SysUser user = (SysUser) rhs;
		return (this.getPassword().equals(user.getPassword()) && this.getUsername().equals(user.getUsername()) && (this.isAccountNonExpired() == user.isAccountNonExpired())
				&& (this.isAccountNonLocked() == user.isAccountNonLocked()) && (this.isCredentialsNonExpired() == user.isCredentialsNonExpired()) && (this.isEnabled() == user.isEnabled()));
	}

	public int hashCode() {
		int code = 9792;
		for (GrantedAuthority authority : getAuthorities()) {
			code = code * (authority.hashCode() % 7);
		}
		if (this.getPassword() != null) {
			code = code * (this.getPassword().hashCode() % 7);
		}
		if (this.getUsername() != null) {
			code = code * (this.getUsername().hashCode() % 7);
		}
		if (this.isAccountNonExpired()) {
			code = code * -2;
		}
		if (this.isAccountNonLocked()) {
			code = code * -3;
		}
		if (this.isCredentialsNonExpired()) {
			code = code * -5;
		}
		if (this.isEnabled()) {
			code = code * -7;
		}
		return code;
	}

	// 组装用户拥有角色成spring security验证
	@Transient
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return authlist;
	}

	@Transient
	public String getRoleId() {
		return roleId;
	}

	public void setRoleId(String roleId) {
		this.roleId = roleId;
	}

	@Transient
	public String getOrgId() {
		return orgId;
	}

	public void setOrgId(String orgId) {
		this.orgId = orgId;
	}

	@Transient
	public String getOrgName() {
		return orgName;
	}

	public void setOrgName(String orgName) {
		this.orgName = orgName;
	}

	@Transient
	public String getParentOrgIds() {
		return parentOrgIds;
	}

	public void setParentOrgIds(String parentOrgIds) {
		this.parentOrgIds = parentOrgIds;
	}

	@Transient
	public String getParentOrgNames() {
		return parentOrgNames;
	}

	public void setParentOrgNames(String parentOrgNames) {
		this.parentOrgNames = parentOrgNames;
	}

	@Transient
	public String getVirtualOrgId() {
		return virtualOrgId;
	}

	public void setVirtualOrgId(String virtualOrgId) {
		this.virtualOrgId = virtualOrgId;
	}

	@Transient
	public String getVirtualOrgName() {
		return virtualOrgName;
	}

	public void setVirtualOrgName(String virtualOrgName) {
		this.virtualOrgName = virtualOrgName;
	}

	@Transient
	public String getPostName() {
		return postName;
	}

	public void setPostName(String postName) {
		this.postName = postName;
	}

	@Transient
	public String getBirthdayFmt() {
		return birthdayFmt;
	}

	public void setBirthdayFmt(String birthdayFmt) {
		this.birthdayFmt = birthdayFmt;
	}

	@Transient
	public String getJoinDateFmt() {
		return joinDateFmt;
	}

	public void setJoinDateFmt(String joinDateFmt) {
		this.joinDateFmt = joinDateFmt;
	}

	@Transient
	public String getExpiredDateFmt() {
		return expiredDateFmt;
	}

	public void setExpiredDateFmt(String expiredDateFmt) {
		this.expiredDateFmt = expiredDateFmt;
	}

	@Transient
	public String getExpiredState() {
		return expiredState;
	}

	public void setExpiredState(String expiredState) {
		this.expiredState = expiredState;
	}

	@Transient
	public String getCompanyName() {
		return companyName;
	}

	public void setCompanyName(String companyName) {
		this.companyName = companyName;
	}

	@Transient
	public String getCompanyCode() {
		return companyCode;
	}

	public void setCompanyCode(String companyCode) {
		this.companyCode = companyCode;

		String manageOrg = PropertiesUtil.get("SYS_MANAGE"); // 系统管理机构编码
		if (StringUtils.equals(companyCode, manageOrg))
			setManage(true);
	}

	@Transient
	public String getCompanyType() {
		return companyType;
	}

	public void setCompanyType(String companyType) {
		this.companyType = companyType;
	}

	@Transient
	public List<SysCompany> getCompanyList() {
		return companyList;
	}

	public void setCompanyList(List<SysCompany> companyList) {
		this.companyList = companyList;
	}
	
	@Transient
	public List<SysUserGroup> getUserGroupList() {
		return userGroupList;
	}

	public void setUserGroupList(List<SysUserGroup> userGroupList) {
		this.userGroupList = userGroupList;
	}
	
	@Transient
	@Temporal(TemporalType.DATE)
	public Date getDutywatchadate() {
		return dutywatchadate;
	}
	public void setDutywatchadate(Date dutywatchadate) {
		this.dutywatchadate = dutywatchadate;
	}

	@Transient
	public String getDutyName() {
		return dutyName;
	}

	public void setDutyName(String dutyName) {
		this.dutyName = dutyName;
	}
}




用户登录将走到这里来

public class MyUserDetailService implements UserDetailsService {

	private static Logger logger = LogManager.getLogger(MyUserDetailService.class);

	// 注入服务
	SysUserService sysUserService;

	SysOrganizationService sysOrganizationService;

	SysUserLoginLogService sysUserLoginLogService;
	
	@Autowired
	SysUserGroupService sysUserGroupService;

	public SysUser loadUserByUsername(String loginId) throws UsernameNotFoundException, DataAccessException {
		SysUser loginUser = new SysUser(); // 登录用户
		if (StringUtils.isNotEmpty(loginId)) {
			logger.info(loginId + " LOGIN...");

			loginUser = sysUserService.findUserByLoginId(loginId); // 通过loginId查询用户
			if (null != loginUser) {
				loginUser.setPostName(DictionaryELTag.getTextByCompanyId("D_Post", loginUser.getPost(),loginUser.getCompanyId()));

				// 查询用户拥有角色
				List<SysRole> roles = sysUserService.findSysRoleByUserId(loginUser.getResourceid());
				if (CollectionUtils.isNotEmpty(roles)){
					loginUser.setRoles(roles);
				}
				if (StringUtils.equals(loginUser.getCompanyType(), "1")) { // 1:公司
					List<SysCompany> companyList = sysOrganizationService.findCompanyByType("2", loginUser.getCompanyId());
					loginUser.setCompanyList(companyList);
				} else if (StringUtils.equals(loginUser.getCompanyType(), "2")) { // 2:服务商
					List<SysCompany> companyList = sysOrganizationService.findCompanyByType("1", loginUser.getCompanyId());
					loginUser.setCompanyList(companyList);
				}
				//查询所在班组(list)
				List<SysUserGroup> groupList = sysUserGroupService.getUserGroupByUserid(loginUser.getResourceid());
				if (CollectionUtils.isNotEmpty(groupList)){
					loginUser.setUserGroupList(groupList);
				}
			} else {
				logger.info("用户名(" + loginId + ")不存在...");
				throw new UsernameNotFoundException("用户名(" + loginId + ")不存在...");
			}
		} else {
			throw new UsernameNotFoundException("用户名不能为空...");
		}

		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		String agent = request.getHeader("User-Agent").toUpperCase(); // 浏览器类型
		String screen_height = request.getParameter("screen_height"); // 分辨率高
		String screen_width = request.getParameter("screen_width"); // 分辨率宽
		String ip = getIpAddr(request);

		SysUserLoginLog loginLog = new SysUserLoginLog(loginUser.getResourceid(), loginUser.getUsername(), ip, agent, screen_height, screen_width, loginUser.getCompanyId());
		sysUserLoginLogService.save(loginLog); // 保存登录日志

		logger.info(loginId + " SUCCESS LOGIN...IP:" + ip);
		return loginUser;
	}

	/**
	 * 通过HttpServletRequest返回IP地址
	 * 
	 * @param request
	 *            HttpServletRequest
	 * @return ip String
	 */
	public String getIpAddr(HttpServletRequest request) throws RuntimeException {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		if (ip == null && ("0:0:0:0:0:0:0:1").equals(ip))
			ip = "127.0.0.1";
		return ip;
	}

	public SysUserService getSysUserService() {
		return sysUserService;
	}

	public void setSysUserService(SysUserService sysUserService) {
		this.sysUserService = sysUserService;
	}

	public SysOrganizationService getSysOrganizationService() {
		return sysOrganizationService;
	}

	public void setSysOrganizationService(SysOrganizationService sysOrganizationService) {
		this.sysOrganizationService = sysOrganizationService;
	}

	public SysUserLoginLogService getSysUserLoginLogService() {
		return sysUserLoginLogService;
	}

	public void setSysUserLoginLogService(SysUserLoginLogService sysUserLoginLogService) {
		this.sysUserLoginLogService = sysUserLoginLogService;
	}

}

返回一个用户,由框架给我们做验证密码,验证是否过期的动作。


登录成功后访问一个url:

走拦截器:

/**
 * TODO:1、拦截器
 */
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

	private FilterInvocationSecurityMetadataSource securityMetadataSource;

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

	public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
		return this.securityMetadataSource;
	}

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

	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 SecurityMetadataSource obtainSecurityMetadataSource() {
		return this.securityMetadataSource;
	}

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

	public void destroy() {
	}

	public void init(FilterConfig arg0) throws ServletException {
	}

}

然后根据请求的url去找到该url对应的角色

public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

	private static Logger logger = LogManager.getLogger(MyInvocationSecurityMetadataSource.class);

	public MyInvocationSecurityMetadataSource() {
		logger.debug("===============================初始化=================================");
	}

	public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
		String url = ((FilterInvocation) object).getRequestUrl(); // 请求地址
		List<String> allUrlList = MemeryCacheManager.getResourceRoleCache().getList("ALL_URL_LIST", String.class); // 从缓存中读取系统中所有url集合,进行对比
		if (CollectionUtils.isEmpty(allUrlList)) {
			SysResourceService resourceService = (SysResourceService) SpringContextUtil.getBean("sysresourceservice");
			allUrlList = resourceService.findAllUrl();
		}

		logger.info((SpringContextUtil.getUser() != null ? "USER:" + SpringContextUtil.getUser().getUsername():"用户没登陆") + " ACCESS_URL:" + url);

		
		if (CollectionUtils.isNotEmpty(allUrlList)) {
			for (String resURL : allUrlList) {
				if (StringUtils.startsWith(url, resURL)) {
					List<ConfigAttribute> atts = MemeryCacheManager.getResourceRoleCache().getList(resURL, ConfigAttribute.class); // 从缓存读取对应请求url,所包含的角色集合
					if (CollectionUtils.isEmpty(atts)) {
						SysResourceService resourceService = (SysResourceService) SpringContextUtil.getBean("sysresourceservice");
						atts = resourceService.findConfigAttByURL(resURL);
					}

					if (CollectionUtils.isNotEmpty(atts)) {
						return atts;
					} else {
						return null;
					}
				}
			} // end for
		}
		return null;
	}

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

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

}
若url对应的就是为null,则所有人都可以访问,否则返回角色的集合,到第三阶段:权限和是否登录的拦截:

public class MyAccessDecisionManager implements AccessDecisionManager {

	/**
	 * 根据用户的角色与URL拥有的角色对比,以确认是否有权限 authentication:访问者所拥有的角色 object:请求URL
	 * configAttributes:请求资源所拥有的角色
	 * 说明:如果在资源管理那边新增了一个url,那么务必给这个url分配一个宿主,否则,所有人(对这个资源无权限的用户)也可以访问
	 */
	public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
		if (configAttributes == null)      //请求资源拥有的角色为null时,所有人都可以去访问这个资源
		{
			return;
		}
		// HttpServletRequest request = invocation.getHttpRequest();

		Object obj = authentication.getPrincipal();
		if (obj.toString().equals("anonymousUser")) {
			FilterInvocation invocation = (FilterInvocation) object;
			String fullUrl = invocation.getFullRequestUrl(); // 外部访问全地址
			String basePath = PropertiesUtil.get("SYS_BASE_PATH");
			if (StringUtils.startsWithIgnoreCase(fullUrl, basePath)) // 外部访问
				return;
		}

		Iterator<ConfigAttribute> ite = configAttributes.iterator(); // 请求URL所对应的角色集合
		while (ite.hasNext()) {
			ConfigAttribute ca = ite.next();
			String needRole = ((SecurityConfig) ca).getAttribute();
			for (GrantedAuthority ga : authentication.getAuthorities()) { // 用户所拥有角色
				if (StringUtils.equals(StringUtils.trim(needRole), StringUtils.trim(ga.getAuthority()))) {
					return;
				}
			}
		}
		throw new AccessDeniedException("请求失败,请检查您的权限...");
	}

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

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

}

有返回的,代表走通了,否则就是将抛出spring security提供的AccessDeniedException异常


本文完毕。















  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值