文章目录
  1. 1. pom.xml添加依赖
  2. 2. 入门环境配置
  3. 3. 指定登录页面
  4. 4. 登出
  5. 5. 登录用户信息从数据库获取
  6. 6. 登录用户信息通过其他方式获取
  7. 7. 密码加密
  8. 8. Remember Me
  9. 9. 配置不受Spring Security管理的URL
  10. 10. 禁用CSRF防御
  11. 11. 获取登录用户信息

在使用Spring Security配置Web应用之前,首先要准备一个基于Maven的Spring框架创建的Web应用(Spring MVC不是必须的),本文的内容都是基于这个前提下的。

pom.xml添加依赖

除了Spring框架本身的一些依赖包,还需要在pom.xml中添加Spring Security的依赖包:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.0.2.RELEASE</version>
</dependency>

入门环境配置

要想使用Spring Security,首先需要在web.xml配置一个过滤器,注意过滤器的filter-name必须是springSecurityFilterChain

1
2
3
4
5
6
7
8
<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>

另外还需要配置Spring Security配置文件,并将这个文件添加到Spring Application Context中。下面是一个最基本的Spring Security配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<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.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">

<http use-expressions="false">
<!-- 访问所有页面都需要有USER权限 -->
<intercept-url pattern="/**" access="ROLE_USER" />
<!-- 登录功能 -->
<form-login />
<!-- 登出功能 -->
<logout />
</http>

<authentication-manager>
<authentication-provider>
<user-service>
<!-- 这里创建两个用户,可以通过用户名密码登录 -->
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>

</beans:beans>

只需完成以上两个配置,启动服务器,用浏览器打开这个Web应用的任意一个页面,都会跳转到一个登录页,这个登录页面是Spring Security自动生成的。
Spring Security默认登录页面
在登录页面输入错误的用户名密码,就会登录失败并有提示。输入正确的用户名密码,则登录成功,就可以进入Web应用的页面。一个最简单的基于Spring Security的Web应用已经完成!

指定登录页面

由于Spring Security默认的登录页面非常简陋,一般不会直接使用,通常会指定一个自定义的登录页面,例如指定一个自己的登录页/login.jsp

1
2
3
4
5
6
7
8
9
10
<http use-expressions="false">
<!-- 登录页面不需要控制权限 -->
<intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<!-- 访问其他所有页面都需要有USER权限 -->
<intercept-url pattern="/**" access="ROLE_USER" />
<!-- 配置登录页面地址login-page、登录失败后的跳转地址authentication-failure-url -->
<form-login login-page='/login.jsp' authentication-failure-url='/login.jsp?error' />
<!-- 登出功能 -->
<logout />
</http>

自定义的登录页面jsp中的登录表单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<c:url value="/login" var="loginUrl" />
<form action="${loginUrl}" method="post">
<c:if test="${param.error != null}">
<p>Invalid username and password.</p>
</c:if>
<c:if test="${param.logout != null}">
<p>You have been logged out.</p>
</c:if>
<p>
<label for="username">Username</label>
<input type="text" id="username" name="username" />
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password" />
</p>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<button type="submit" class="btn">Log in</button>
</form>

登录表单提交的页面地址是/login,method是POST请求,登录请求需要提交usernamepassword两个参数。为了安全,防止恶意的CSRF攻击,Spring Security需要校验form表单中的hidden域提交的内容。

登出

Spring Security配置文件中的<logout />用于处理登出。

页面中的登出按钮:

1
2
3
4
5
<c:url value="/logout" var="logoutUrl" />
<form action="${logoutUrl}" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<input type="submit" value="退出" />
</form>

登出请求地址/logout,method是POST请求。

登录用户信息从数据库获取

上文的登录用户的用户名、密码、ROLE都是配置在Spring Security的xml配置文件中的,在实际使用中,一般不会将用户信息直接配置在xml文件中,而是通过其他方式获取,例如数据库。

Spring Security提供了一个便捷的方式通过数据库获取用户信息,即org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl,它是org.springframework.security.core.userdetails.UserDetailsService接口的一个实现类,只要配置相关的DataSource和SQL语句就能从数据库获取到用户信息:

1
2
3
4
5
6
7
8
<authentication-manager>
<authentication-provider user-service-ref='userDetailsService' />
</authentication-manager>
<beans:bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="usersByUsernameQuery" value="select username, password, true from t_user where username = ?" />
<beans:property name="authoritiesByUsernameQuery" value="select username, role from t_user_role where username = ?" />
</beans:bean>

以上配置还可以简化为:

1
2
3
4
5
6
7
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username, password, true from t_user where username = ?"
authorities-by-username-query="select username, role from t_user_role where username = ?" />
</authentication-provider>
</authentication-manager>

登录用户信息通过其他方式获取

如果用户信息的来源并不是数据库,那么就需要自己实现org.springframework.security.core.userdetails.UserDetailsService接口的loadUserByUsername方法,即通过用户名获取用户信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class UserDetailsServiceImpl implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 以下可以替换成用其他方式获取用户信息
if(username.equals("xxg")) {
Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
auths.add(authority);
User user = new User(username, "123456", auths);
return user;
} else {
throw new UsernameNotFoundException("用户不存在");
}
}
}

将该实现类配置在Spring Security配置文件中:

1
2
3
4
<authentication-manager>
<authentication-provider user-service-ref='userDetailsService' />
</authentication-manager>
<beans:bean id="userDetailsService" class="com.xxg.UserDetailsServiceImpl" />

密码加密

明文保存密码通常是不安全的,在Spring Security中可以配置密码的加密方法。下面以MD5加密密码为例。

针对密码加密,Spring Security提供了org.springframework.security.crypto.password.PasswordEncoder接口。我们需要实现PasswordEncoder接口,实现我们自定义的加密方法,这样Spring Security在接收到用户登录请求后,会调用这个实现类,从而判断密码是否正确:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class PasswordEncoderImpl implements PasswordEncoder {

@Override
public String encode(CharSequence rawPassword) {
try {
// MD5加密密码
return DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}

@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encode(rawPassword).equals(encodedPassword);
}
}

在Spring Security配置文件中配置上面的实现类:

1
2
3
4
5
6
7
<authentication-manager>
<authentication-provider>
<password-encoder ref="passwordEncoder" />
...
</authentication-provider>
</authentication-manager>
<beans:bean id="passwordEncoder" class="com.xxg.PasswordEncoderImpl" />

Remember Me

目前很多网站登录时都有“记住我”、“下次自动登录”这样的chechbox,用户登录成功后下次再访问网站就不需要重复登录,对用户来说非常方便。这个功能在Spring Security中实现起来非常简单。

首先在登录页面的HTML中加入一个checkbox,name为remember-me

1
<input type="checkbox" id="keep-login" name="remember-me"><label for="keep-login">Remember Me</label>

然后在Spring Security配置文件中加入:

1
2
3
4
<http use-expressions="false">
...
<remember-me />
</http>

以上配置完成后Remember Me功能就生效了。

默认的Remember Me有效时间时14天,还可以通过token-validity-seconds属性自定义有效时长(单位:秒):

1
<remember-me token-validity-seconds="2592000" />

配置不受Spring Security管理的URL

如果Web应用中有某些URL不需要被Spring Security管理,例如一些静态文件,或者无需登录即可查看的页面,可以对这些URL配置security="none"

1
2
3
4
5
6
7
8
<http pattern="/resources/css/**" security="none"/>
<http pattern="/resources/images/**" security="none"/>
<http pattern="/resources/js/**" security="none"/>
<http use-expressions="false">
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login />
<logout />
</http>

禁用CSRF防御

Spring Security默认启用CSRF防御,要求每个POST请求都要都要带上CSRF token参数,如果感觉比较麻烦或者网站安全性要求不高,可以配置禁用:

1
2
3
4
<http use-expressions="false">
...
<csrf disabled="true" />
</http>

获取登录用户信息

获取用户名:

1
2
httpServletRequest.getRemoteUser();  // Servlet标准,推荐使用
SecurityContextHolder.getContext().getAuthentication().getName();

获取用户ROLE:

1
SecurityContextHolder.getContext().getAuthentication().getAuthorities();

判断用户是否拥有ROLE:

1
httpServletRequest.isUserInRole("ADMIN");

文章目录
  1. 1. pom.xml添加依赖
  2. 2. 入门环境配置
  3. 3. 指定登录页面
  4. 4. 登出
  5. 5. 登录用户信息从数据库获取
  6. 6. 登录用户信息通过其他方式获取
  7. 7. 密码加密
  8. 8. Remember Me
  9. 9. 配置不受Spring Security管理的URL
  10. 10. 禁用CSRF防御
  11. 11. 获取登录用户信息