spring security 提供系统安全功能
文章分类:Java编程
1.概述
由于 spring security 是在权限系统开发关闭后补上去的所以只使用了 spring security 中的提供的一部分内容;
完成功能包括:用户校验、注销功能、Session 失效处理、防止一个用户同一时间内多次登录系统、保护系统资源(防止绕过登录访问资源)。
2.代码片段
- <?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.5.xsd
- http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
- <!-- http安全配置 -->
- <http servlet-api-provision="true">
- <intercept-url pattern="/**/*.jpg" filters="none"/>
- <intercept-url pattern="/**/*.png" filters="none"/>
- <intercept-url pattern="/**/*.gif" filters="none"/>
- <intercept-url pattern="/**/*.css" filters="none"/>
- <intercept-url pattern="/**/*.js" filters="none"/>
- <intercept-url pattern="/servlet/CheckLoginCodeServlet" filters="none"/>
- <intercept-url pattern="/servlet/SessionImage" filters="none"/>
- <intercept-url pattern="/login/login.jsp" filters="none"/>
- <intercept-url pattern="/login/logout.jsp" filters="none"/>
- <intercept-url pattern="/**" access="ROLE_ADMIN"/>
- <form-login login-page="/login/login.jsp"
- authentication-failure-url="/login/login.jsp"
- default-target-url="/login.do"
- always-use-default-target="true"/>
- <logout/>
- <concurrent-session-control/>
- </http>
- <beans:bean id="jaasAuthenticationProvider"
- class="org.springframework.security.providers.jaas.JaasAuthenticationProvider">
- <custom-authentication-provider/>
- <beans:property name="loginConfig" value="/WEB-INF/login.conf" />
- <beans:property name="loginContextName" value="JAASTest" />
- <beans:property name="callbackHandlers">
- <beans:list>
- <beans:bean class="org.springframework.security.providers.jaas.JaasNameCallbackHandler" />
- <beans:bean class="org.springframework.security.providers.jaas.JaasPasswordCallbackHandler" />
- </beans:list>
- </beans:property>
- <beans:property name="authorityGranters">
- <beans:list>
- <beans:bean class="com.hw.msds.login.businessimp.AuthorityGranterImpl" />
- </beans:list>
- </beans:property>
- </beans:bean>
- <beans:bean id="sessionTimeoutFilter" class="com.hw.msds.login.businessimp.SessionTimeoutFilter">
- <custom-filter before="CONCURRENT_SESSION_FILTER" />
- </beans:bean>
- </beans:beans>
- <form method="post" action="<%=basePath%>j_spring_security_check" οnsubmit="return loginsubmit();">
- 用户名: <input id="j_username" name="j_username" type="text" class="user" />
- 密 码:<input id="j_password" name="j_password" type="password" class="user" />
- <input id="submitbtn" type="submit" value="登 录" class="btn_login"/>
- </form>
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <base href="<%=basePath%>">
- <title>登出</title>
- <meta http-equiv="pragma" content="no-cache">
- <meta http-equiv="cache-control" content="no-cache">
- <meta http-equiv="expires" content="0">
- <script type="text/javascript">
- function init() {
- window.top.location.target="_top";
- window.top.location.href="j_spring_security_logout";
- }
- window.οnlοad=init;
- </script>
- </head>
- <body>
- session失效请 <a href="j_spring_security_logout" target="_top">重新登录</a>
- </body>
- </html>
- JAASTest {
- com.hw.msds.login.businessimp.LoginModuleImpl required
- driver="oracle.jdbc.driver.OracleDriver"
- url="jdbc:oracle:thin:msds/msds@192.168.1.154:1521:msds"
- db_username="msds"
- db_password="msds"
- debug="true";
- };
- import java.security.Principal;
- import java.util.HashSet;
- import java.util.Set;
- import org.springframework.security.providers.jaas.AuthorityGranter;
- public class AuthorityGranterImpl implements AuthorityGranter {
- public Set grant(Principal principal) {
- Set rtnSet = new HashSet();
- if (principal.getName().equals("TEST_PRINCIPAL")) {
- rtnSet.add("ROLE_ADMIN");
- }
- return rtnSet;
- }
- }
import java.security.Principal;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.providers.jaas.AuthorityGranter;
public class AuthorityGranterImpl implements AuthorityGranter {
public Set grant(Principal principal) {
Set rtnSet = new HashSet();
if (principal.getName().equals("TEST_PRINCIPAL")) {
rtnSet.add("ROLE_ADMIN");
}
return rtnSet;
}
}
- import java.security.Principal;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.util.Map;
- import javax.security.auth.Subject;
- import javax.security.auth.callback.Callback;
- import javax.security.auth.callback.CallbackHandler;
- import javax.security.auth.callback.NameCallback;
- import javax.security.auth.callback.PasswordCallback;
- import javax.security.auth.callback.TextInputCallback;
- import javax.security.auth.login.LoginException;
- import javax.security.auth.spi.LoginModule;
- import org.springframework.security.context.SecurityContextHolder;
- import com.hw.msds.base.sys.Password;
- import com.hw.msds.base.sys.SystemBuffer;
- import com.hw.msds.base.util.DB;
- public class LoginModuleImpl implements LoginModule {
- private Subject subject;
- private CallbackHandler callbackHandler;
- private Map sharedState;
- private Map options;
- private String url;
- private String driver;
- private String db_username;
- private String db_password;
- private String password;
- private String user;
- public boolean abort() throws LoginException {
- return true;
- }
- public boolean commit() throws LoginException {
- return true;
- }
- public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
- this.subject = subject;
- this.callbackHandler = callbackHandler;
- this.sharedState = sharedState;
- this.options = options;
- url = (String)options.get("url");
- driver = (String)options.get("driver");
- db_username = (String)options.get("db_username");
- db_password = (String)options.get("db_password");
- try {
- TextInputCallback textCallback = new TextInputCallback("prompt");
- NameCallback nameCallback = new NameCallback("prompt");
- PasswordCallback passwordCallback = new PasswordCallback("prompt", false);
- callbackHandler.handle(new Callback[] {textCallback, nameCallback, passwordCallback});
- password = new String(passwordCallback.getPassword());
- user = nameCallback.getName();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- public boolean login() throws LoginException {
- System.out.println("driver " + driver);
- System.out.println("url " + url);
- System.out.println("db_username " + db_username);
- System.out.println("db_password " + db_password);
- System.out.println("user " + user);
- System.out.println("password " + password);
- String dpwd="";
- String dtype="";
- boolean flag=false;
- // 校验用户
- String sql = "select tp.name, tps.secretcode, tc.type from tperson tp inner join tcorp tc on tp.corpid = tc.objid inner join tpersonpsw tps on tp.objid = tps.objid where tp.name = ?";
- Connection conn = DB.getConnection(driver, url, db_username, db_password);
- PreparedStatement stat = null;
- ResultSet rs = null;
- try {
- stat = conn.prepareStatement(sql);
- stat.setString(1, user);
- rs = stat.executeQuery();
- while (rs != null && rs.next()) {
- dpwd = rs.getString("secretcode");
- dtype = rs.getString("type");
- flag = true;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- try {
- if (rs != null) {
- rs.close();
- }
- if (stat != null) {
- stat.close();
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- DB.closeConnection(conn);
- if (flag == false) {
- throw new LoginException("用户名不存在!");
- }
- if (!dpwd.equals(Password.createPassword(password))) {
- throw new LoginException("密码错误!");
- }
- SystemBuffer.userInfo.put(user + "_password", dpwd);
- SystemBuffer.userInfo.put(user + "_logintype", dtype);
- subject.getPrincipals().add(new Principal() {
- public String getName() {
- return "TEST_PRINCIPAL";
- }
- });
- return true;
- }
- public boolean logout() throws LoginException {
- return true;
- }
- }
import java.security.Principal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import org.springframework.security.context.SecurityContextHolder;
import com.hw.msds.base.sys.Password;
import com.hw.msds.base.sys.SystemBuffer;
import com.hw.msds.base.util.DB;
public class LoginModuleImpl implements LoginModule {
private Subject subject;
private CallbackHandler callbackHandler;
private Map sharedState;
private Map options;
private String url;
private String driver;
private String db_username;
private String db_password;
private String password;
private String user;
public boolean abort() throws LoginException {
return true;
}
public boolean commit() throws LoginException {
return true;
}
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;
url = (String)options.get("url");
driver = (String)options.get("driver");
db_username = (String)options.get("db_username");
db_password = (String)options.get("db_password");
try {
TextInputCallback textCallback = new TextInputCallback("prompt");
NameCallback nameCallback = new NameCallback("prompt");
PasswordCallback passwordCallback = new PasswordCallback("prompt", false);
callbackHandler.handle(new Callback[] {textCallback, nameCallback, passwordCallback});
password = new String(passwordCallback.getPassword());
user = nameCallback.getName();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public boolean login() throws LoginException {
System.out.println("driver " + driver);
System.out.println("url " + url);
System.out.println("db_username " + db_username);
System.out.println("db_password " + db_password);
System.out.println("user " + user);
System.out.println("password " + password);
String dpwd="";
String dtype="";
boolean flag=false;
// 校验用户
String sql = "select tp.name, tps.secretcode, tc.type from tperson tp inner join tcorp tc on tp.corpid = tc.objid inner join tpersonpsw tps on tp.objid = tps.objid where tp.name = ?";
Connection conn = DB.getConnection(driver, url, db_username, db_password);
PreparedStatement stat = null;
ResultSet rs = null;
try {
stat = conn.prepareStatement(sql);
stat.setString(1, user);
rs = stat.executeQuery();
while (rs != null && rs.next()) {
dpwd = rs.getString("secretcode");
dtype = rs.getString("type");
flag = true;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (rs != null) {
rs.close();
}
if (stat != null) {
stat.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
DB.closeConnection(conn);
if (flag == false) {
throw new LoginException("用户名不存在!");
}
if (!dpwd.equals(Password.createPassword(password))) {
throw new LoginException("密码错误!");
}
SystemBuffer.userInfo.put(user + "_password", dpwd);
SystemBuffer.userInfo.put(user + "_logintype", dtype);
subject.getPrincipals().add(new Principal() {
public String getName() {
return "TEST_PRINCIPAL";
}
});
return true;
}
public boolean logout() throws LoginException {
return true;
}
}
- import java.io.IOException;
- import javax.servlet.FilterChain;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpSession;
- import org.springframework.security.ui.AbstractProcessingFilter;
- import org.springframework.security.ui.SpringSecurityFilter;
- import org.springframework.security.ui.savedrequest.SavedRequest;
- import org.springframework.security.util.PortResolver;
- import org.springframework.security.util.PortResolverImpl;
- public class SessionTimeoutFilter extends SpringSecurityFilter {
- private PortResolver portResolver = new PortResolverImpl();
- private String sessionTimeoutUrl;
- public void doFilterHttp(HttpServletRequest request,
- HttpServletResponse response, FilterChain chain)
- throws IOException, ServletException {
- if (this.isSessionExpired(request)) {
- System.out.println("!!!!!!!!!!!!!!!!!!!!!!!! session过期");
- this.processRequest(request, response);
- } else {
- chain.doFilter(request, response);
- }
- }
- protected String determineSessionTimeoutUrl(HttpServletRequest request) {
- return (sessionTimeoutUrl != null) ? sessionTimeoutUrl : "/login/logout.jsp";
- }
- protected boolean isSessionExpired(HttpServletRequest request) {
- return (request.getRequestedSessionId() != null)
- && !request.isRequestedSessionIdValid();
- }
- protected void processRequest(HttpServletRequest request,
- HttpServletResponse response) throws IOException {
- HttpSession session = request.getSession();
- SavedRequest savedRequest = new SavedRequest(request, portResolver);
- session.setAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, new RuntimeException("连接超时,如果需要继续操作请重新登录系统!"));
- session.setAttribute(AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
- String targetUrl = determineSessionTimeoutUrl(request);
- targetUrl = request.getContextPath() + targetUrl;
- response.sendRedirect(response.encodeRedirectURL(targetUrl));
- }
- public int getOrder() {
- return 0;
- }
- }
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.savedrequest.SavedRequest;
import org.springframework.security.util.PortResolver;
import org.springframework.security.util.PortResolverImpl;
public class SessionTimeoutFilter extends SpringSecurityFilter {
private PortResolver portResolver = new PortResolverImpl();
private String sessionTimeoutUrl;
public void doFilterHttp(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (this.isSessionExpired(request)) {
System.out.println("!!!!!!!!!!!!!!!!!!!!!!!! session过期");
this.processRequest(request, response);
} else {
chain.doFilter(request, response);
}
}
protected String determineSessionTimeoutUrl(HttpServletRequest request) {
return (sessionTimeoutUrl != null) ? sessionTimeoutUrl : "/login/logout.jsp";
}
protected boolean isSessionExpired(HttpServletRequest request) {
return (request.getRequestedSessionId() != null)
&& !request.isRequestedSessionIdValid();
}
protected void processRequest(HttpServletRequest request,
HttpServletResponse response) throws IOException {
HttpSession session = request.getSession();
SavedRequest savedRequest = new SavedRequest(request, portResolver);
session.setAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, new RuntimeException("连接超时,如果需要继续操作请重新登录系统!"));
session.setAttribute(AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
String targetUrl = determineSessionTimeoutUrl(request);
targetUrl = request.getContextPath() + targetUrl;
response.sendRedirect(response.encodeRedirectURL(targetUrl));
}
public int getOrder() {
return 0;
}
}
3.说明
a. 用户校验和用户信息加载
我是用了JAAS进行用户名密码校验,所有校验成功的用户都被分配了ROLE_ADMIN角色,校验失败抛出异常。
校验成功后我指定了程序跳转至login.do,login.do作用就是加载用户相关信息(包括用户所属部门、所属公司、可操作的模块以及对应的权限信息)
b. 系统注销
login.do 判断如果是注销,清空当前用户session,并转向 j_spring_security_logout 。
c. Session 失效处理
详见 SessionTimeoutFilter.java , 判断是否有 sessionid 存在如果存在判断是否过期,如果过期则调整至登录页面并给出提示信息,如果没有sessionid说明用户未登录过不错任何处理。
d. 防止一个用户同一时间内多次登录系统
spring security 提供的功能 详见<concurrent-session-control/>
e. 保护系统资源(防止绕过登录访问资源)
详见 <http> 部分, 注意当用户身份校验成功后,就会被赋予ROLE_ADMIN角色,意味着这他就能够访问所有页面(给个用户访问权限内的所有页面)
4.标注
上面是我在最近项目中web安全方面的的一点点经验,希望大家多提建议。
参考文献:
spring security 安全开发手册
http://www.family168.com/oa/springsecurity/html/index.html