使用spring security 實現許可權的驗證
阿新 • • 發佈:2019-01-09
這是在上個公司做專案的時候用到的許可權驗證,但由於時間太長,修改次數較多,現在只剩下了一部分程式碼以及配置檔案,總的來說,其實現思路是可以借鑑的,現在來想想,其實自己實現也並不是很難的,而且自定義性非常大,而且能夠實現頁面是否具有增刪改的許可權以及相應圖示文字的顯示與否。具體思路其實大都類似,下面我還是大概說下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{
@Override
public String encodePassword(String origPwd, Object salt)
throws DataAccessException {
// System.out.println("origPwd:"+origPwd+" salt:"+salt);
return origPwd;
}
@Override
public 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;
/*@Override
public 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()]);
}*/
/*@Override
public String getPassword() {
// TODO Auto-generated method stub
return userPass;
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return 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{
@Override
public 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;
}
@Override
public 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;
@Override
public 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;
}
@Override
public Collection getConfigAttributeDefinitions() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean supports(Class arg0) {
// TODO Auto-generated method stub
return true;
}
@Override
public 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 {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public 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");
}
}
@Override
public 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 110928
function 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 110928
function 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>