使用spring security 实现权限的验证

来源:互联网 发布:网络配电箱250x300 编辑:程序博客网 时间:2024/05/05 22:22

        这是在上个公司做项目的时候用到的权限验证,但由于时间太长,修改次数较多,现在只剩下了一部分代码以及配置文件,总的来说,其实现思路是可以借鉴的,现在来想想,其实自己实现也并不是很难的,而且自定义性非常大,而且能够实现页面是否具有增删改的权限以及相应图标文字的显示与否。具体思路其实大都类似,下面我还是大概说下spring security究竟是怎么一个东东。

        其共使用到了5张表,实体如下:

1.用户表Users    CREATE TABLE `users` (       -- 账号是否有限 1. 是 0.否       `enable` int(11) default NULL,       `password` varchar(255) default NULL,       `account` varchar(255) default NULL,       `id` int(11) NOT NULL auto_increment,       PRIMARY KEY  (`id`)    )    2.角色表Roles   CREATE TABLE `roles` (     `enable` int(11) default NULL,     `name` varchar(255) default NULL,     `id` int(11) NOT NULL auto_increment,     PRIMARY KEY  (`id`)   )    3 用户_角色表users_roles   CREATE TABLE `users_roles` (     --用户表的外键     `uid` int(11) default NULL,     --角色表的外键     `rid` int(11) default NULL,     `urId` int(11) NOT NULL auto_increment,     PRIMARY KEY  (`urId`),     KEY `rid` (`rid`),     KEY `uid` (`uid`),    CONSTRAINT `users_roles_ibfk_1` FOREIGN KEY (`rid`) REFERENCES `roles` (`id`),    CONSTRAINT `users_roles_ibfk_2` FOREIGN KEY (`uid`) REFERENCES `users` (`id`)   )    4.资源表resources   CREATE TABLE `resources` (     `memo` varchar(255) default NULL,     -- 权限所对应的url地址     `url` varchar(255) default NULL,     --优先权     `priority` int(11) default NULL,     --类型     `type` int(11) default NULL,     --权限所对应的编码,例201代表发表文章     `name` varchar(255) default NULL,     `id` int(11) NOT NULL auto_increment,     PRIMARY KEY  (`id`)   )     5.角色_资源表roles_resources    CREATE TABLE `roles_resources` (      `rsid` int(11) default NULL,      `rid` int(11) default NULL,      `rrId` int(11) NOT NULL auto_increment,      PRIMARY KEY  (`rrId`),      KEY `rid` (`rid`),      KEY `roles_resources_ibfk_2` (`rsid`),      CONSTRAINT `roles_resources_ibfk_2` FOREIGN KEY (`rsid`) REFERENCES `resources` (`id`),      CONSTRAINT `roles_resources_ibfk_1` FOREIGN KEY (`rid`) REFERENCES `roles` (`id`)      )

二、系统配置

   所需要的jar包,请自行到官网下载,我用的是Spring Security3.1.0.RC1版的。

web.xml

<!-- Spring -->  <context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml,classpath:applicationContext-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>


application-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-2.0.xsd                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd"><beans:bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener" /> <!-- auto-config = true 则使用from-login. 如果不使用该属性 则默认为http-basic(没有session).            access-denied-page:出错后跳转到的错误页面;       -->     <http auto-config="true" access-denied-page="/403.jsp">                  <intercept-url pattern="/**" filters="none"/>         <!--  <intercept-url pattern="/u2/user!Code.action" filters="none"/> -->      <!--   <intercept-url pattern="/u2/**" access="ROLE_SUPERVISOR" />          <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" /> -->          <!-- logout-success-url:成功注销后跳转到的页面; -->  <logout/> <form-login login-page="/u2/login.jsp" authentication-failure-url="/u2/login_json.jsp?error=2" default-target-url="/u2/user!loginSpring.action" />     </http> <authentication-manager alias="authenticationManager"/>        <!--    Usernames/Passwords are        rod/koala        dianne/emu        scott/wombat        peter/opal    --> <!--     <authentication-provider>        <password-encoder hash="md5"/>        <user-service>            <user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />        <user name="dianne" password="65d15fe9156f9c4bbffd98085992a44e" authorities="ROLE_USER,ROLE_TELLER" />            <user name="scott" password="2b58af6dddbd072ed27ffc86725d7d3a" authorities="ROLE_USER" />            <user name="peter" password="22b5c9accc6e1ba628cedc63a72d57f8" authorities="ROLE_USER" />    </user-service></authentication-provider>  --><!-- <authentication-provider>      <jdbc-user-service data-source-ref="dataSource"      users-by-username-query="SELECT U.NAME AS 'USERNAME', U.password, U.DISABLED AS 'enabled' FROM User U where U.name=?"      authorities-by-username-query="SELECT U.username, R.name as 'authority' FROM User U JOIN Authority A ON u.id = A.userId JOIN Role R ON R.id = A.roleId WHERE U.username=?"/>  </authentication-provider>  --><authentication-provider user-service-ref="securityManager">  <password-encoder ref="myPasswordEncoder"><!-- <salt-source /> --></password-encoder> </authentication-provider>  <beans:bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">          <beans:property name="allowIfAllAbstainDecisions" value="false"/>          <beans:property name="decisionVoters">              <beans:list>                  <beans:bean class="org.springframework.security.vote.RoleVoter"/>                  <beans:bean class="org.springframework.security.vote.AuthenticatedVoter"/>              </beans:list>          </beans:property>      </beans:bean>          <beans:bean id="resourceSecurityInterceptor" class="org.springframework.security.intercept.web.FilterSecurityInterceptor">          <beans:property name="authenticationManager" ref="authenticationManager"/>          <beans:property name="accessDecisionManager" ref="accessDecisionManager"/>          <beans:property name="objectDefinitionSource" ref="secureResourceFilterInvocationDefinitionSource" />          <beans:property name="observeOncePerRequest" value="false" />          <custom-filter after="LAST" />      </beans:bean>                    <beans:bean id="secureResourceFilterInvocationDefinitionSource" class="net.b2c.u.interceptor.SecureResourceFilterInvocationDefinitionSource" >     <beans:property name="securityManager" ref="securityManager"/>       </beans:bean> <beans:bean id="myPasswordEncoder" class="net.b2c.u.util.PwdEncoder" /><beans:bean id="securityManager" class="net.b2c.u.dao.hibernate.SecurityManagerSupport" parent="daoTempldate" /></beans:beans>


这里authentication-provider有三种方式,一种是直接写在文件里面的,一种是写sql语句,另外则是本文介绍的。

PasswordEncoder

package net.b2c.u.util;import org.springframework.dao.DataAccessException;import org.springframework.security.providers.encoding.PasswordEncoder;public class PwdEncoder implements PasswordEncoder{@Overridepublic String encodePassword(String origPwd, Object salt)throws DataAccessException {//System.out.println("origPwd:"+origPwd+"  salt:"+salt);return origPwd;}@Overridepublic boolean isPasswordValid(String encPwd, String origPwd, Object salt)throws DataAccessException {//System.out.println("encPwd:"+encPwd+"   origPwd:"+origPwd+"  salt:"+salt);boolean b=encPwd.equals(origPwd);//System.out.println("b:"+b);return b;}}

user部分

private Set<Srole> roles;/*@Overridepublic GrantedAuthority[] getAuthorities() {List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>(roles.size());for (Srole role : roles) {grantedAuthorities.add(new GrantedAuthorityImpl(role.getName()));}return grantedAuthorities.toArray(new GrantedAuthority[roles.size()]);}*//*@Overridepublic String getPassword() {// TODO Auto-generated method stubreturn userPass;}@Overridepublic String getUsername() {// TODO Auto-generated method stubreturn loginName;}


SecurityManagerSupport

package net.b2c.u.dao.hibernate;import java.util.HashMap;import java.util.List;import java.util.Map;import net.b2c.u.model.Sresource;import net.b2c.u.model.UserU;import org.springframework.dao.DataAccessException;import org.springframework.orm.hibernate3.support.HibernateDaoSupport;import org.springframework.security.userdetails.UserDetails;import org.springframework.security.userdetails.UserDetailsService;import org.springframework.security.userdetails.UsernameNotFoundException;public class SecurityManagerSupport extends HibernateDaoSupport implements UserDetailsService ,SecurityManager{@Overridepublic UserDetails loadUserByUsername(String userName)throws UsernameNotFoundException, DataAccessException {List<UserU> users = getHibernateTemplate().find("FROM UserU user WHERE user.loginName = ? AND user.disabled = false", userName);  if(users.isEmpty()) {            throw new UsernameNotFoundException("User " + userName + " has no GrantedAuthority");        }return null;}@Overridepublic Map<String, String> loadUrlAuthorities() {Map<String, String> urlAuthorities = new HashMap<String, String>();          List<Sresource> urlResources = getHibernateTemplate().find("FROM Sresource resource WHERE resource.type = ?", "URL");          for(Sresource resource : urlResources) {              urlAuthorities.put(resource.getValue(), resource.getRoleAuthorities());          }          return urlAuthorities;  }}


SecureResourceFilterInvocationDefinitionSource

package net.b2c.u.interceptor;import java.util.Collection;import java.util.Iterator;import java.util.Map;import net.b2c.u.dao.hibernate.SecurityManager;import org.springframework.beans.factory.InitializingBean;import org.springframework.security.ConfigAttributeDefinition;import org.springframework.security.ConfigAttributeEditor;import org.springframework.security.intercept.web.FilterInvocation;import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;import org.springframework.security.util.AntUrlPathMatcher;import org.springframework.security.util.RegexUrlPathMatcher;import org.springframework.security.util.UrlMatcher;public class SecureResourceFilterInvocationDefinitionSource implements FilterInvocationDefinitionSource, InitializingBean{ private UrlMatcher urlMatcher;        private boolean useAntPath = true;            private boolean lowercaseComparisons = true;        private SecurityManager securityManager;@Overridepublic ConfigAttributeDefinition getAttributes(Object filter)throws IllegalArgumentException { FilterInvocation filterInvocation = (FilterInvocation) filter;          String requestURI = filterInvocation.getRequestUrl();          Map<String, String> urlAuthorities = this.getUrlAuthorities(filterInvocation);                    String grantedAuthorities = null;          for(Iterator<Map.Entry<String, String>> iter = urlAuthorities.entrySet().iterator(); iter.hasNext();) {              Map.Entry<String, String> entry = iter.next();              String url = entry.getKey();                            if(urlMatcher.pathMatchesUrl(url, requestURI)) {                  grantedAuthorities = entry.getValue();                  break;              }                        }                    if(grantedAuthorities != null) {              ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();              configAttrEditor.setAsText(grantedAuthorities);              return (ConfigAttributeDefinition) configAttrEditor.getValue();          }  return null;}@Overridepublic Collection getConfigAttributeDefinitions() {// TODO Auto-generated method stubreturn null;}@Overridepublic boolean supports(Class arg0) {// TODO Auto-generated method stubreturn true;}@Overridepublic void afterPropertiesSet() throws Exception {// default url matcher will be RegexUrlPathMatcher          this.urlMatcher = new RegexUrlPathMatcher();                    if (useAntPath) {  // change the implementation if required              this.urlMatcher = new AntUrlPathMatcher();          }                    // Only change from the defaults if the attribute has been set          if ("true".equals(lowercaseComparisons)) {              if (!this.useAntPath) {                  ((RegexUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(true);              }          } else if ("false".equals(lowercaseComparisons)) {              if (this.useAntPath) {                  ((AntUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(false);              }          }          }@SuppressWarnings("unchecked")      private Map<String, String> getUrlAuthorities(FilterInvocation filterInvocation) {        //  ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();      //    return (Map<String, String>)servletContext.getAttribute("urlAuthorities");  return getSecurityManager().loadUrlAuthorities();    }public UrlMatcher getUrlMatcher() {return urlMatcher;}public void setUrlMatcher(UrlMatcher urlMatcher) {this.urlMatcher = urlMatcher;}public boolean isUseAntPath() {return useAntPath;}public void setUseAntPath(boolean useAntPath) {this.useAntPath = useAntPath;}public boolean isLowercaseComparisons() {return lowercaseComparisons;}public void setLowercaseComparisons(boolean lowercaseComparisons) {this.lowercaseComparisons = lowercaseComparisons;}public SecurityManager getSecurityManager() {return securityManager;}public void setSecurityManager(SecurityManager securityManager) {this.securityManager = securityManager;}}

SecurityManager

package net.b2c.u.dao.hibernate;import java.util.Map;public interface SecurityManager { public Map<String, String> loadUrlAuthorities();}


最后,spring security是不带验证码的,如果项目里面使用到的话,则需要另外想办法,我在这里说下我们的实现思路,就是写一个filter,使验证码的判断在一个filter里面进行,如果正确,则接着往下走,否则的话,直接跳到登录页面。

LoginCodeFilter

package net.b2c.u.interceptor;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 javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class LoginCodeFilter implements Filter {@Overridepublic void destroy() {// TODO Auto-generated method stub}@Overridepublic void doFilter(ServletRequest servletRequest,ServletResponse servletResponse, FilterChain filterChain)throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;String loginCode = request.getParameter("code");String sessionCode = "";Object obj = request.getSession().getAttribute("checkCode");if (null != obj) {sessionCode = obj.toString();}if (null != loginCode && !"".equals(loginCode)&& loginCode.equalsIgnoreCase(sessionCode)) {filterChain.doFilter(request, response);}else {response.sendRedirect(request.getContextPath()+"/u2/login_json.jsp?error=3");}}@Overridepublic void init(FilterConfig arg0) throws ServletException {// TODO Auto-generated method stub}}


最后,说下spring security 的用户名和密码字段名字是固定的,j_username和j_password,看登录页面:
loginSpring.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"    pageEncoding="utf-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><link href="css/base.css" rel="stylesheet" type="text/css" /><style type="text/css">* { padding: 0; margin: 0;}body { font: 12px/150% Arial,Verdana,"\5b8b\4f53";color: #333;}h2,h3 { font-size: 14px;}a { color: #333;}a:hover { color: #f00;}table { empty-cells: show; }.w { width: 990px; margin: 0 auto;}.w {width: 1220px; }#entry .mt { height: 33px; background: url(img/tit_regist.jpg) #D1D1D1 repeat-x 0 -34px;}.mt,.mc {overflow: hidden; zoom: 1;}#entry .mt h2 { float: left; height: 33px; padding-left: 15px; background: url(img/tit_regist.jpg) no-repeat 0 0; line-height: 33px; }.mt h2, .smt h3 { font-family: "microsoft yahei"; }#entry .mt b { float: right; width: 10px; height: 33px; background: url(img/tit_regist.jpg) no-repeat 0 -68px; }#entry .mc { padding: 40px 40px 30px 40px; border: solid #D1D1D1; border-width: 0 1px 1px;}#entry .form { float: left; width: 540px; overflow: hidden; }#entry .item { padding-top: 5px; height: 50px; line-height: 26px; }#entry .label { width: 100px; text-align: right; font-size: 14px;  float: left;}.fl { float: left; }#entry .text { width: 240px; height: 16px; padding: 4px 3px; border: 1px solid #bbb; font-size: 14px; font-family: arial;}.clr { display: block; overflow: hidden; clear: both; }.form input,.from label { float: left; font-size: 12px;}#entry .blank { width: 16px; height: 16px; margin: 2px 5px 0; }.invisible { visibility: hidden; }#entry .text-1 { width: 100px; }#entry #autoentry { height: 40px; }#entry .btn-entry { width: 87px; height: 35px; background: url(img/bg_regist.jpg) no-repeat -155px -150px; font-size: 14px; font-weight: bold; color: #fff; }#guide { float: right; width: 300px; height: 290px; padding: 0px 0 20px 40px; border-left: 1px solid #E7E7E7; }h5 {font-size: 12px;}#guide .content { color: #666; padding: 10px 0 0; }#guide .btn-personal { width: 137px; height: 35px; background: url(img/bg_regist.jpg) no-repeat 0 -186px; line-height: 35px; margin: 20px auto; font-weight: bold; font-size: 14px; margin-top: 35px; }.btn-link { display: block; overflow: hidden; text-align: center; }.btn-img, .button { display: inline-block; margin: 0; padding: 0; border: 0; text-align: center; cursor: pointer; }</style><script type="text/javascript" src="../js/jquery-1.7.1.js"></script><script type="text/javascript" src="../js/jquery.form.js"></script><script type="text/javascript" src="js/common.js"></script><script type="text/javascript"> function goInBox(){ document.getElementById("j_username").focus(); document.onkeydown = keyDown;}//记住密码实现方法 by hyy 110928function cgUserName(){ var key = $('#j_username').val();var value = $('#j_password').val();if(document.getElementById("remember").checked){ SetCookie(key,value,720);SetCookie("bc_username111_111"+key,document.getElementById("remember").checked,720);}else{  if(key!="") {  delCookie(key,"/"); }delCookie("bc_username111_111"+key,"/"); }}//加载cookie中的用户名和密码 by hyy 110928function loads(){var key = $('#j_username').val();var value = getCookie(key);if(value!=null && value!="null") {$('#j_password').val(value);}document.getElementById("remember").checked=getCookie('bc_username111_111'+key);} function loginASD(){  if($("#j_username").val()==""){ alert("用户名不能为空"); return;}if($("#j_password").val()==""){ alert("密码不能为空"); return;}if($("#code").val()==""){ alert("验证码不能为空"); return;}      $.ajax( { // dataType : 'json',  url : "../j_spring_security_check",  cache : false,  type : 'POST',  data : {   "j_username":$("#j_username").val(),   "j_password":$("#j_password").val(),   "code":$("#code").val()  },  success : function(data) {      if(data==1){    alert("登入成功");    cgUserName();    window.location.href="member.jsp";   }else   if(data==2){    alert("用户名或密码错误");   }else{    alert("验证码不正确");   }        },  error : function(XMLHttpRequest, textStatus, errorThrown) {   alert(errorThrown);   alert("您的session已过期,请重新登入");   window.location.href = "../";  } });           }function changeNumber(){ document.getElementById("CheckNumber").src="user!Code.action?a="+Math.random();}</script></head><jsp:include page="head.jsp"></jsp:include><body><body onload="goInBox();"><div style="margin-bottom:20px; margin-top:0px">    <form id="formLogin" name="formLogin" action="../j_spring_security_check" method="post">    <div class="w" id="entry">  <div class="mt">   <h2>用户登录</h2>   <b></b>  </div>  <div class="mc" style="padding-top:20px;">        <div class="form">     <div class="item">    <span class="label">用户名:</span>    <div class="fl">       <input type="text" id="j_username" name="j_username" class="text" tabindex="1" value="" onchange="loads()">       <label class="blank invisible"></label>       <span class="clr"></span>    </div>   </div>       <div class="item">    <span class="label">密码:</span>    <div class="fl">       <input type="password" id="j_password" name="j_password" class="text" tabindex="2">       <label class="blank invisible"></label>       <label><a href="###" class="flk13">忘记密码?</a></label>       <span class="clr"></span>    </div>   </div>          <div class="item">    <span class="label">验证码:</span>    <div class="fl">        <input type="text" id="code" style="ime-mode:disabled" name="code" class="text text-1" tabindex="6">        <label class="blank invisible"></label>         <label class="ftx23"> <img name="CheckNumber" id="CheckNumber" title="看不清,点击换一张" style="cursor:pointer;width=60;height=20;" onClick="changeNumber()" align="absmiddle" border=0 src="user!Code.action" width="100" height="26" ></label>         <span class="clr"></span>    </div>   </div>      <div class="item" id="autoentry"> <span class="label"> </span>    <div class="fl">     <table cellpadding="0" cellspacing="0">      <tbody>       <tr>        <td style="padding:0px"><input type="checkbox" value="1" name="remember" id="remember"></td>        <td style="padding:0px">记住密码</td>       </tr>      </tbody>     </table>    </div>          </div>              <div class="item">    <span class="label"> </span>            <input type="button"  class="btn-img btn-entry" value="登录" tabindex="8" onclick="loginASD()">          </div>        </div>        <div id="guide">          <h5>还不是商城用户?</h5>          <div class="content">现在免费注册成为商城用户,便能立刻享受便宜又放心的购物乐趣。</div>          <a href="zc.jsp" class="btn-link btn-personal">注册新用户</a> </div>        <span class="clr"></span> </div>    </div>  </form>        </div></body><jsp:include page="foot.jsp"></jsp:include><script type="text/javascript"></script></html>




 

原创粉丝点击