spring security

论坛 期权论坛 脚本     
匿名技术用户   2020-12-27 12:00   11   0

spring security 提供系统安全功能

文章分类:Java编程

1.概述

由于 spring security 是在权限系统开发关闭后补上去的所以只使用了 spring security 中的提供的一部分内容;

完成功能包括:用户校验、注销功能、Session 失效处理、防止一个用户同一时间内多次登录系统、保护系统资源(防止绕过登录访问资源)。

2.代码片段

Applicationcontext-security.xml代码 复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans:beans xmlns="http://www.springframework.org/schema/security"
  3. xmlns:beans="http://www.springframework.org/schema/beans"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="
  6. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  7. http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
  8. <!-- http安全配置 -->
  9. <http servlet-api-provision="true">
  10. <intercept-url pattern="/**/*.jpg" filters="none"/>
  11. <intercept-url pattern="/**/*.png" filters="none"/>
  12. <intercept-url pattern="/**/*.gif" filters="none"/>
  13. <intercept-url pattern="/**/*.css" filters="none"/>
  14. <intercept-url pattern="/**/*.js" filters="none"/>
  15. <intercept-url pattern="/servlet/CheckLoginCodeServlet" filters="none"/>
  16. <intercept-url pattern="/servlet/SessionImage" filters="none"/>
  17. <intercept-url pattern="/login/login.jsp" filters="none"/>
  18. <intercept-url pattern="/login/logout.jsp" filters="none"/>
  19. <intercept-url pattern="/**" access="ROLE_ADMIN"/>
  20. <form-login login-page="/login/login.jsp"
  21. authentication-failure-url="/login/login.jsp"
  22. default-target-url="/login.do"
  23. always-use-default-target="true"/>
  24. <logout/>
  25. <concurrent-session-control/>
  26. </http>
  27. <beans:bean id="jaasAuthenticationProvider"
  28. class="org.springframework.security.providers.jaas.JaasAuthenticationProvider">
  29. <custom-authentication-provider/>
  30. <beans:property name="loginConfig" value="/WEB-INF/login.conf" />
  31. <beans:property name="loginContextName" value="JAASTest" />
  32. <beans:property name="callbackHandlers">
  33. <beans:list>
  34. <beans:bean class="org.springframework.security.providers.jaas.JaasNameCallbackHandler" />
  35. <beans:bean class="org.springframework.security.providers.jaas.JaasPasswordCallbackHandler" />
  36. </beans:list>
  37. </beans:property>
  38. <beans:property name="authorityGranters">
  39. <beans:list>
  40. <beans:bean class="com.hw.msds.login.businessimp.AuthorityGranterImpl" />
  41. </beans:list>
  42. </beans:property>
  43. </beans:bean>
  44. <beans:bean id="sessionTimeoutFilter" class="com.hw.msds.login.businessimp.SessionTimeoutFilter">
  45. <custom-filter before="CONCURRENT_SESSION_FILTER" />
  46. </beans:bean>
  47. </beans:beans>
Login.jsp 片段代码 复制代码
  1. <form method="post" action="<%=basePath%>j_spring_security_check" οnsubmit="return loginsubmit();">
  2. 用户名: <input id="j_username" name="j_username" type="text" class="user" />
  3. 密&nbsp;&nbsp;码:<input id="j_password" name="j_password" type="password" class="user" />
  4. <input id="submitbtn" type="submit" value="登 录" class="btn_login"/>
  5. </form>
Logout.jsp代码 复制代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
  2. <%
  3. String path = request.getContextPath();
  4. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
  5. %>
  6. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  7. <html>
  8. <head>
  9. <base href="<%=basePath%>">
  10. <title>登出</title>
  11. <meta http-equiv="pragma" content="no-cache">
  12. <meta http-equiv="cache-control" content="no-cache">
  13. <meta http-equiv="expires" content="0">
  14. <script type="text/javascript">
  15. function init() {
  16. window.top.location.target="_top";
  17. window.top.location.href="j_spring_security_logout";
  18. }
  19. window.οnlοad=init;
  20. </script>
  21. </head>
  22. <body>
  23. session失效请 <a href="j_spring_security_logout" target="_top">重新登录</a>
  24. </body>
  25. </html>
Login.conf代码 复制代码
  1. JAASTest {
  2. com.hw.msds.login.businessimp.LoginModuleImpl required
  3. driver="oracle.jdbc.driver.OracleDriver"
  4. url="jdbc:oracle:thin:msds/msds@192.168.1.154:1521:msds"
  5. db_username="msds"
  6. db_password="msds"
  7. debug="true";
  8. };
Authoritygranterimpl.java代码 复制代码
  1. import java.security.Principal;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. import org.springframework.security.providers.jaas.AuthorityGranter;
  5. public class AuthorityGranterImpl implements AuthorityGranter {
  6. public Set grant(Principal principal) {
  7. Set rtnSet = new HashSet();
  8. if (principal.getName().equals("TEST_PRINCIPAL")) {
  9. rtnSet.add("ROLE_ADMIN");
  10. }
  11. return rtnSet;
  12. }
  13. }
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;
    }
}
Loginmoduleimpl.java代码 复制代码
  1. import java.security.Principal;
  2. import java.sql.Connection;
  3. import java.sql.PreparedStatement;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.util.Map;
  7. import javax.security.auth.Subject;
  8. import javax.security.auth.callback.Callback;
  9. import javax.security.auth.callback.CallbackHandler;
  10. import javax.security.auth.callback.NameCallback;
  11. import javax.security.auth.callback.PasswordCallback;
  12. import javax.security.auth.callback.TextInputCallback;
  13. import javax.security.auth.login.LoginException;
  14. import javax.security.auth.spi.LoginModule;
  15. import org.springframework.security.context.SecurityContextHolder;
  16. import com.hw.msds.base.sys.Password;
  17. import com.hw.msds.base.sys.SystemBuffer;
  18. import com.hw.msds.base.util.DB;
  19. public class LoginModuleImpl implements LoginModule {
  20. private Subject subject;
  21. private CallbackHandler callbackHandler;
  22. private Map sharedState;
  23. private Map options;
  24. private String url;
  25. private String driver;
  26. private String db_username;
  27. private String db_password;
  28. private String password;
  29. private String user;
  30. public boolean abort() throws LoginException {
  31. return true;
  32. }
  33. public boolean commit() throws LoginException {
  34. return true;
  35. }
  36. public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
  37. this.subject = subject;
  38. this.callbackHandler = callbackHandler;
  39. this.sharedState = sharedState;
  40. this.options = options;
  41. url = (String)options.get("url");
  42. driver = (String)options.get("driver");
  43. db_username = (String)options.get("db_username");
  44. db_password = (String)options.get("db_password");
  45. try {
  46. TextInputCallback textCallback = new TextInputCallback("prompt");
  47. NameCallback nameCallback = new NameCallback("prompt");
  48. PasswordCallback passwordCallback = new PasswordCallback("prompt", false);
  49. callbackHandler.handle(new Callback[] {textCallback, nameCallback, passwordCallback});
  50. password = new String(passwordCallback.getPassword());
  51. user = nameCallback.getName();
  52. } catch (Exception e) {
  53. throw new RuntimeException(e);
  54. }
  55. }
  56. public boolean login() throws LoginException {
  57. System.out.println("driver " + driver);
  58. System.out.println("url " + url);
  59. System.out.println("db_username " + db_username);
  60. System.out.println("db_password " + db_password);
  61. System.out.println("user " + user);
  62. System.out.println("password " + password);
  63. String dpwd="";
  64. String dtype="";
  65. boolean flag=false;
  66. // 校验用户
  67. 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 = ?";
  68. Connection conn = DB.getConnection(driver, url, db_username, db_password);
  69. PreparedStatement stat = null;
  70. ResultSet rs = null;
  71. try {
  72. stat = conn.prepareStatement(sql);
  73. stat.setString(1, user);
  74. rs = stat.executeQuery();
  75. while (rs != null && rs.next()) {
  76. dpwd = rs.getString("secretcode");
  77. dtype = rs.getString("type");
  78. flag = true;
  79. }
  80. } catch (SQLException e) {
  81. e.printStackTrace();
  82. } finally {
  83. try {
  84. if (rs != null) {
  85. rs.close();
  86. }
  87. if (stat != null) {
  88. stat.close();
  89. }
  90. } catch (SQLException e) {
  91. e.printStackTrace();
  92. }
  93. }
  94. DB.closeConnection(conn);
  95. if (flag == false) {
  96. throw new LoginException("用户名不存在!");
  97. }
  98. if (!dpwd.equals(Password.createPassword(password))) {
  99. throw new LoginException("密码错误!");
  100. }
  101. SystemBuffer.userInfo.put(user + "_password", dpwd);
  102. SystemBuffer.userInfo.put(user + "_logintype", dtype);
  103. subject.getPrincipals().add(new Principal() {
  104. public String getName() {
  105. return "TEST_PRINCIPAL";
  106. }
  107. });
  108. return true;
  109. }
  110. public boolean logout() throws LoginException {
  111. return true;
  112. }
  113. }
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;
    }
}
Sessiontimeoutfilter.java代码 复制代码
  1. import java.io.IOException;
  2. import javax.servlet.FilterChain;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import javax.servlet.http.HttpSession;
  7. import org.springframework.security.ui.AbstractProcessingFilter;
  8. import org.springframework.security.ui.SpringSecurityFilter;
  9. import org.springframework.security.ui.savedrequest.SavedRequest;
  10. import org.springframework.security.util.PortResolver;
  11. import org.springframework.security.util.PortResolverImpl;
  12. public class SessionTimeoutFilter extends SpringSecurityFilter {
  13. private PortResolver portResolver = new PortResolverImpl();
  14. private String sessionTimeoutUrl;
  15. public void doFilterHttp(HttpServletRequest request,
  16. HttpServletResponse response, FilterChain chain)
  17. throws IOException, ServletException {
  18. if (this.isSessionExpired(request)) {
  19. System.out.println("!!!!!!!!!!!!!!!!!!!!!!!! session过期");
  20. this.processRequest(request, response);
  21. } else {
  22. chain.doFilter(request, response);
  23. }
  24. }
  25. protected String determineSessionTimeoutUrl(HttpServletRequest request) {
  26. return (sessionTimeoutUrl != null) ? sessionTimeoutUrl : "/login/logout.jsp";
  27. }
  28. protected boolean isSessionExpired(HttpServletRequest request) {
  29. return (request.getRequestedSessionId() != null)
  30. && !request.isRequestedSessionIdValid();
  31. }
  32. protected void processRequest(HttpServletRequest request,
  33. HttpServletResponse response) throws IOException {
  34. HttpSession session = request.getSession();
  35. SavedRequest savedRequest = new SavedRequest(request, portResolver);
  36. session.setAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, new RuntimeException("连接超时,如果需要继续操作请重新登录系统!"));
  37. session.setAttribute(AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
  38. String targetUrl = determineSessionTimeoutUrl(request);
  39. targetUrl = request.getContextPath() + targetUrl;
  40. response.sendRedirect(response.encodeRedirectURL(targetUrl));
  41. }
  42. public int getOrder() {
  43. return 0;
  44. }
  45. }
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

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP