感谢原文出处:http://liukai.iteye.com/blog/982088/
该项目为maven项目,请先学习maven
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wyc</groupId>
<artifactId>SpringSecurity</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>SpringSecurity Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<!-- http://mvnrepository.com/artifact/jstl/jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<!-- http://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- http://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.6.RELEASE</version>
</dependency>
<!-- http://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.6.RELEASE</version>
</dependency>
<!-- http://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<!-- 只在编译和测试时运行 -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.28</version>
</dependency>
</dependencies>
<build>
<finalName>SpringSecurity</finalName>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
</plugin>
</plugins>
</build>
</project>
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<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>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-security.xml
/WEB-INF/applicationContext.xml
/WEB-INF/spring-servlet.xml
</param-value>
</context-param>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
要想启用spring security,必须在web.xml中加入以下filter
<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.xml为spring security的配置文件,applicationContext.xml为spring配置文件
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-security.xml
/WEB-INF/applicationContext.xml
/WEB-INF/spring-servlet.xml
</param-value>
</context-param>
<?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:context="http://www.springframework.org/schema/context"
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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- <beans:import resource="/spring-database.xml"/> -->
<http use-expressions="true" auto-config="true">
<intercept-url pattern="/auth/login" access="permitAll"/>
<intercept-url pattern="/test/admin" access="hasRole('ROLE_ADMIN')"/>
<intercept-url pattern="/test/common" access="hasRole('ROLE_USER')"/>
<form-login login-page="/auth/login"
authentication-failure-url="/auth/login?error=true"
login-processing-url="/check_action"
default-target-url="/test/common"
always-use-default-target="true"
username-parameter="username"
password-parameter="password"/>
<logout logout-success-url="/auth/login"
invalidate-session="true"
logout-url="/auth/logout"/>
<!-- <access-denied-handler error-page="/auth/denied"/> -->
<!-- <session-management>
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
</session-management> -->
</http>
<authentication-manager>
<authentication-provider user-service-ref="customerUserDetailService">
<!-- <jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username username,password password,enabled enabled from _user where username=?"
authorities-by-username-query="select username username,role role from _user_role where username=?"/> -->
</authentication-provider>
</authentication-manager>
<beans:bean id="customerUserDetailService" class="com.wyc.service.CustomerUserDetailsService"></beans:bean>
</beans:beans>
<intercept-url pattern="/auth/login" access="permitAll"/>
<intercept-url pattern="/test/admin" access="hasRole('ROLE_ADMIN')"/>
<intercept-url pattern="/test/common" access="hasRole('ROLE_USER')"/>
需要注意的是我们使用了
SpringEL表达式
来指定角色的访问.
以下是表达式对应的用法.
表达式 说明
hasRole([role]) 返回 true 如果当前主体拥有特定角色。
hasAnyRole([role1,role2]) 返回 true 如果当前主体拥有任何一个提供的角色 (使用逗号分隔的字符串队列)
principal 允许直接访问主体对象,表示当前用户
authentication 允许直接访问当前 Authentication对象 从SecurityContext中获得
permitAll 一直返回true
denyAll 一直返回false
isAnonymous() 如果用户是一个匿名登录的用户 就会返回 true
isRememberMe() 如果用户是通过remember-me 登录的用户 就会返回 true
isAuthenticated() 如果用户不是匿名用户就会返回true
isFullyAuthenticated() 如果用户不是通过匿名也不是通过remember-me登录的用户时, 就会返回true。
自定义登录页面配置
<form-login login-page="/auth/login"
authentication-failure-url="/auth/login?error=true"
login-processing-url="/check_action"
default-target-url="/test/common"
always-use-default-target="true"
username-parameter="username"
password-parameter="password"/>
其中,login-page属性配置自定义的登录界面url,此处url指向所有人均可访问的/auth/login,这是controller中方法,指向登录页面,详情可看后文
authentication-failure属性配置的是登录失败的处理结果url,此处登录失败跳转至登录页面,并且附加error参数
login-proccessing-url属性对应登录页面表单的form中的action属性值
default-target-url属性配置默认的登录成功后的页面
always-use-default-target属性可能配置的是无论用户从哪个页面进入登录界面,最后的结果都是到达default-target-url的界面(有时候从某个页面进入登录界面,登录成功后会跳转至登录前的页面),以上俩个属性的作用纯属个人理解,文档中原文如下:
If a form login isn’t prompted by an attempt to access a protected resource, the default-target-url option comes into play.
This is the URL the user will be taken to after successfully logging in, and defaults to "/".
You can also configure things so that the user always ends up at this page (regardless of whether the login was "on-demand"
or they explicitly chose to log in) by setting the always-use-default-target attribute to "true".
This is useful if your application always requires that the user starts at a "home" page
username-parameter对应表单中用户名输入框的name属性值
同理,password-parameter对应表单中密码输入框的name属性值
配置注销账户:
<logout logout-success-url="/auth/login"
invalidate-session="true"
logout-url="/auth/logout"/>
invalidate-session配置的是
开启了session失效功能.
配置默认无权限页面
<access-denied-handler error-page="/auth/denied"/>
<session-management>
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
</session-management>
<authentication-manager>
<authentication-provider user-service-ref="customerUserDetailService">
<!-- <jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username username,password password,enabled enabled from _user where username=?"
authorities-by-username-query="select username username,role role from _user_role where username=?"/> -->
</authentication-provider>
</authentication-manager>
spring-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<beans:bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</beans:bean>
</beans:beans>
配置jsp页面的访问路径及后缀(因为我们在controller中只返回jsp页面的名称)
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.wyc"></context:component-scan>
<mvc:annotation-driven/>
</beans:beans>
context:annotation-config开启注解配置
context:component-scan配置需要扫描的包
mvc:annotation-driven开启注解驱动
TestController.java
package com.wyc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@RequestMapping("/test")
@Controller
public class TestController {
@RequestMapping(value = "/common",method = RequestMethod.GET)
public String common(){
return "common";
}
@RequestMapping(value = "/admin",method = RequestMethod.GET)
public String admin(){
return "admin";
}
}
此处勿加上responseBody注解,加入后的结果:
以tom身份登录的结果:
LoginLogoutController.java
package com.wyc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@RequestMapping("/auth")
@Controller
public class LoginLogoutController {
@RequestMapping(value ="/login",method = RequestMethod.GET)
public String login(@RequestParam(value= "error",required = false) boolean error,ModelMap model){
if(error){
model.put("error", "用户名或密码错误");
}else{
model.put("error","");
}
return "login";
}
@RequestMapping(value = "denied" , method = RequestMethod.GET)
public String denied(){
return "accessDenied";
}
}
DbUser.java
package com.wyc.model;
public class DbUser {
private String username;
private String password;
private Integer access;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAccess() {
return access;
}
public void setAccess(Integer access) {
this.access = access;
}
}
UserDao.java
package com.wyc.dao;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.wyc.model.DbUser;
@Repository("userDao")
public class UserDao {
public DbUser getDatabase(String username){
List<DbUser> dbUsers = internalDb();
for(DbUser dbUser : dbUsers){
if(dbUser.getUsername().equals(username)){
return dbUser;
}
}
throw new RuntimeException("user is not exist");
}
private List<DbUser> internalDb() {
List<DbUser> dbUsers = new ArrayList<DbUser>();
DbUser dbUser = new DbUser();
dbUser.setUsername("tom");
dbUser.setPassword("tom");
dbUser.setAccess(1);
dbUsers.add(dbUser);
dbUser = new DbUser();
dbUser.setUsername("mike");
dbUser.setPassword("mike");
dbUser.setAccess(2);
dbUsers.add(dbUser);
return dbUsers;
}
}
CustomerUserDetailsService.java
package com.wyc.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.wyc.dao.UserDao;
import com.wyc.model.DbUser;
public class CustomerUserDetailsService implements UserDetailsService{
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
UserDetails userDetails = null;
try{
DbUser dbUser = userDao.getDatabase(username);
userDetails = new User(dbUser.getUsername(), dbUser.getPassword().toLowerCase(),
true, true, true, true, getAuthorities(dbUser.getAccess()));
}catch(Exception e){
throw new UsernameNotFoundException("Error in retrieving user");
}
return userDetails;
}
private Collection<GrantedAuthority> getAuthorities(Integer access) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
//每个用户都具有ROLE_USER权限
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
// 如果参数access为1.则拥有ROLE_ADMIN权限
if (access.compareTo(1) == 0) {
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
return authorities;
}
}
login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" isELIgnored="false"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%
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>My JSP 'login.jsp' starting page</title>
</head>
<body>
<form method="post" action="<c:url value='check_action' />">
<input type="text" name="username">
<input type="password" name="password">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<input type="submit" value="提交">
</form>
</body>
</html>
<%@ 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>My JSP 'success.jsp' starting page</title>
</head>
<body>
每个人都可以访问的页面
<a href = "<%=basePath %>test/admin">管理员界面</a>
<a href = "<%=basePath %>auth/login">退出登录</a>
</body>
</html>
admin.jsp
<%@ 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>My JSP 'success.jsp' starting page</title>
</head>
<body>
管理员界面
<a href = "<%=basePath %>auth/login">退出登录</a>
</body>
</html>
accessDenied.jsp
<%@ 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>My JSP 'success.jsp' starting page</title>
</head>
<body>
权限不足
<a href= "<%=basePath %>auth/login">退出登录</a>
</body>
</html>
至此,代码完毕
以下是结果:
使用tom登录:
点击管理员界面:
mike登录后点击管理员界面(此处无权限配置被注释):