首页 > 代码库 > Spring框架:Spring安全

Spring框架:Spring安全

在传统的Web开发中,安全性的代码都是分散在各个模块中的,这样不方便管理,而且有时候可能会漏掉一个地方导致安全漏洞。为了解决这个问题,有人发明了Spring Security。它的作用是将业务逻辑中有关安全的代码全都移动到一个模块中集中管理。本质上是AOP的一个子集。


过滤URL

为了过滤URL,首先要在web.xml中加入一个过滤器。filter-name不能随便填写,因为它和另外一个bean的名称是一样的。

<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>


下面配置的作用是拦截所有的请求。

<security:http auto-config="true">
  <intercept-url pattern="/**" access="ROLE_VIP"/>
</security:http>
auto-config的作用相当于<form-login/><http-basic/><logout/>,它会给你自动生成一个登陆页面。access="ROLE_VIP"表示只连接身份为ROLE_VIP的用户,ROLE_VIP这个名称是由我们自己定义的。


上面的例子中,Spring框架自动生成了一个登陆页面,但是不太美观。因此,我们需要自己定义登陆页面。

<http auto-config="true">
  <form-login login-processing-url="/static/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t"/>
</http>


自定义的登陆页面代码如下。表单中的j_username、j_password、_spring_security_remember_me这几个名字是框架已经定死的,自己不能随意改动。
<spring:url var="authUrl" value=http://www.mamicode.com/"/static/j_spring_security_check"/>>


认证表达式。上面的例子中使用了access="ROLE_VIP"限制了VIP用户的访问,这还不够,你可以在access中填写复杂的Spring表达式实现更强大的功能。比如下面的例子:
<http auto-config="true" use-expressions="true">
  <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN') and hasIpAddress('192.168.1.2')"/>
</http>

注意一定要开启use-expression=true。


认证表达式中支持的函数有:denyAll、hasAnyRole、hasRole、hasIpAddress、isAnonymouse、isAuthenticated、isFullyAuthenticated、isRememberMe、permitAll,支持的变量有autentication、principal。


HTTPS拦截。有些特殊的URL必须要用HTTPS安全连接。写法如下。

<intercept-url pattern="/admin" requires-channel="https"/>
<intercept-url pattern="/public" requires-channel="http"/>


保护视图

在JSP文件访问与认证有关的变量,或者根据访问者的身份显示不同的内容。


访问认证细节。比如访问登陆的用户名。

<sec:authentication property="principal.username"/>


根据不同的身份显示不同的内容。请看下面的例子。

<sec:authorize access="hasRole('ROLE_VIP')">
  You are VIP.
</sec:authorize>


认证方式

Spring支持的认证方式有:基于xml配置、基于JDBC、基于LDAP、OpenID、CAS、X509、JAAS。


基于xml配置。将用户名和密码写在配置文件中。

<security:user-service id="userService">
  <user name="root" password="123456" authorities="ROLE_VIP,ROLE_ADMIN"/>
  <user name="test" password="test" authorities="ROLE_VIP"/>
</security:user-service>

<security:authentication-manager>
  <authentication-provider user-service-ref="userService"/>
</security:authentication-manager>


基于JDBC。

<security:jdbc-user-service id="userService" data-source-ref="dataSource" users-by-username-query="select username,password,enabled from user where username=?" authorities-by-username-query="select username,authoritiy from user_auth"/>


基于LDAP。

<security:authentication-manager alias="authenticationManager">
  <security:ldap-authentication-provider user-search-filter="(uid={0})" group-search-filter="member={0}">
    <security:password-compare hash="md5"/>
    
    <security:ldap-server url="ldap://example.com/dc=test"/>
  </security:ldap-authentication-provider>
</security:authentication-manager>


记住登陆

<http auto-config="true">
  <remember-me key="myVipKey" token-validity-seconds="86400"/>
</http>

myVipKey是Cookie中的令牌键名,令牌中保存了过期时间、用户名、令牌密钥。


拦截方法调用

开启注解方式的安全拦截。

<global-method-security secured-annotations="enabled"/>


根据身份进行拦截。

@Secured("ROLE_VIP")
public void test(){}


过滤返回值。

@PostFilter("filterObject.user.username == principal.name")
public List<User> getUserList(){}


横切授权

<global-method-security>
  <protect-pointcut access="ROLE_VIP" expression="execution(@com.example.User * *.*(String))"/>
</global-method-security>

Spring框架:Spring安全