springMVC security Demo
springMVC 结合权限控制。
项目目录结构(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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.royal</groupId>
<artifactId>springMVCSecurityDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springMVCSecurityDemo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>
<!-- Spring 3 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.0.7.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.0.7.RELEASE</version>
</dependency>
<!-- 标签库 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>3.0.7.RELEASE</version>
</dependency>
</dependencies>
</project>
载入后的jar包
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>SpringMVCSecurityDemo Application</display-name>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-dataAccess.xml
/WEB-INF/spring-security.xml
</param-value>
</context-param>
<!-- spring mvc -->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- spring security -->
<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>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
其中applicationContext-dataAccess.xml是进行访问mysql数据库的dataSource配置,等会儿再说。
先看其他的配置文件。
spring-mvc.xml
<beans xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
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" >
<context:component-scan base-package="com.royal.controller"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
两点:1.扫描Cotroller 2. 访问目标文件时自动于其添加前缀/pages/和后缀.jsp
OK,看下控制类。
LoginController.java
package com.royal.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;
/**
* 登录控制类
* @author Royal
*
*/
@Controller
public class LoginController {
@RequestMapping(value = "/login" , method = RequestMethod.GET)
public String gotoLogin(ModelMap model){
return "login";
}
/**
* 访问地址:http://ip:port/project/welcome
* @param model
* @return
*/
@RequestMapping(value = "/user" , method = RequestMethod.GET)
public String gotoUser(ModelMap model){
model.addAttribute("message", "USER");
//寻找页面user.jsp,地址变为:http://ip:port/project/user.jsp
return "user";
}
@RequestMapping(value = "/admin" , method = RequestMethod.GET)
public String gotoAdmin(ModelMap model){
model.addAttribute("message", "ADMIN");
//寻找页面admin.jsp,地址变为:http://ip:port/project/admin.jsp
return "admin";
}
}
至此,springMVC配置完成。
接下来结合进权限控制。
spring-security.xml
<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-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<http auto-config="true">
<!-- 拥有ROLE_ADMIN的角色权限才可以访问 -->
<intercept-url pattern="/admin" access="ROLE_ADMIN" />
<!-- 拥有ROLE_USER的角色权限才可以访问 -->
<intercept-url pattern="/user" access="ROLE_USER"/>
<!-- 用户注销时,跳转到登录界面 -->
<logout logout-success-url="/login"/>
<!-- 记住我,两周之内不必登陆 -->
<remember-me key="_spring_security_remember_me"/>
<!-- 防御会话伪造攻击 -->
<session-management session-fixation-protection="none"/>
<!-- 取消默认的spring自带的spring_security_login页面,自定义登录界面. 默认访问user页面 -->
<intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<form-login login-page="/login"
authentication-failure-url="/login?error=true"
default-target-url="/user" />
</http>
<authentication-manager>
<authentication-provider>
<!-- 硬编码形式 -->
<!-- <user-service> -->
<!-- 以admin的形式登录,你就有ROLE_USER,ROLE_ADMIN两个用户角色的权限进行访问 -->
<!-- <user name="admin" password="admin" authorities="ROLE_USER,ROLE_ADMIN" /> -->
<!-- 以user的形式登录,你只有ROLE_USER的用户角色的权限进行访问 -->
<!-- <user name="user" password="user" authorities="ROLE_USER"/> -->
<!-- </user-service> -->
<jdbc-user-service
data-source-ref="dataSource"
users-by-username-query="select username,password,enabled from tb_user where username = ? and enabled = 1"
authorities-by-username-query="select u.username,r.name from tb_user u join tb_user_role ur on u.id = ur.user_id join tb_role r on r.id = ur.role_id where u.username = ?" />
</authentication-provider>
</authentication-manager>
</beans:beans>
以上有2种形式的权限声明,显然第一种硬编码形式的我们是不可能用到实际项目中的;所以采用第二种结合数据库的形式
OK,这样的话,那就看下数据库的结构。
mysql数据库结构
存在三张表
1. tb_role 权限表 id为主键
2.tb_user 用户表 id为主键
3.中间表,权限表和用户表间的桥梁 user_id 和 role_id 同时为主键,且user_id为tb_user表id的外键、role_id为tb_role表id的外键。
OK,表的关系已经构建完成,以上描述的关系形式是:royal作为“游客”,只有ROLE_USER标识的权限;admin作为“管理员”,同时拥有ROLE_USER和ROLE_ADMIN标识的权限。
好,回到配置文件中。
注意到这段代码了吗? 注意SQL语句的写法,要查询的表别写错了。
<jdbc-user-service
data-source-ref="dataSource"
users-by-username-query="select username,password,enabled from tb_user where username = ? and enabled = 1"
authorities-by-username-query="select u.username,r.name from tb_user u join tb_user_role ur on u.id = ur.user_id join tb_role r on r.id = ur.role_id where u.username = ?" />
现在大概能猜到它的意思了吧?对,它就是相当于硬编码时配置的用户名和密码形式。
<!-- 硬编码形式 -->
<user-service>
以admin的形式登录,你就有ROLE_USER,ROLE_ADMIN两个用户角色的权限进行访问
<user name="admin" password="admin" authorities="ROLE_USER,ROLE_ADMIN" />
以user的形式登录,你只有ROLE_USER的用户角色的权限进行访问
<user name="user" password="user" authorities="ROLE_USER"/>
</user-service>
好了,现在再看下连接mysql数据库的配置。
applicationContext-dataAccess.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/db_springsecurity" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>
</beans>
简单点就好了,对应好自己的数据库名,连接名和密码。
配置呢,就差不多这样了,最后给几个页面测试,其中一个页面需要声明一下,那就是登录页面,我没有采用默认的,我重写了,所以你在上面的spring-security.xml中可以看到下面这段代码的说明。
<!-- 取消默认的spring自带的spring_security_login页面,自定义登录界面. 默认访问user页面 -->
<intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<form-login login-page="/login"
authentication-failure-url="/login?error=true"
default-target-url="/user" />
login.jsp---登录页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page isELIgnored="false"%>
<%
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>springMVCSecurityDemo</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<style type="text/css">
div.error {
width: 260px;
border: 2px solid red;
background-color: yellow;
text-align: center;
}
div.hide {
display: none;
}
</style>
</head>
<body onload='document.f.j_username.focus();'>
<!-- 注意这个form的每个参数 -->
<form name="f" action="<%=path%>/j_spring_security_check" method='POST'>
<fieldset>
<legend>登录</legend>
用户:<input type="text" name="j_username"
value="${sessionScope['SPRING_SECURITY_LAST_USERNAME']}" /><br />
密码:<input type="password" name="j_password" /><br /> <input
type="checkbox" name="_spring_security_remember_me" />两周之内不必登陆<br />
<input type="submit" value="登录" /> <input type="reset" value="重置" />
</fieldset>
</form>
<!-- 如果登录失败 -->
<div class="error ${param.error == true ? '' : 'hide'}">
登陆失败<br>
${sessionScope['SPRING_SECURITY_LAST_EXCEPTION'].message}
</div>
</body>
</html>
user.jsp---游客页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page isELIgnored="false"%>
<%@ taglib prefix="sec"
uri="http://www.springframework.org/security/tags"%>
<%
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>springMVCSecurityDemo</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<h1>Message:${message}</h1>
<sec:authorize ifAllGranted="ROLE_ADMIN,ROLE_USER">
admin and user
</sec:authorize>
<br/>
<sec:authorize ifAnyGranted="ROLE_ADMIN,ROLE_USER">
admin or user
</sec:authorize>
<br/>
<sec:authorize ifNotGranted="ROLE_ADMIN">
not admin
</sec:authorize>
<br/>
<a href="j_spring_security_logout">注销</a>
</body>
</html>
<!-- ifAllGranted,只有当前用户同时拥有ROLE_ADMIN和ROLE_USER两个权限时,才能显示标签内部内容。 -->
<!-- ifAnyGranted,如果当前用户拥有ROLE_ADMIN或ROLE_USER其中一个权限时,就能显示标签内部内容。 -->
<!-- ifNotGranted,如果当前用户没有ROLE_ADMIN时,才能显示标签内部内容。 -->
看到了吗?我们不但在访问路径上做了权限的控制,同时在页面中也做了相应的权限控制。
admin.jsp---管理员页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page isELIgnored="false" %>
<%
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>springMVCSecurityDemo</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<h1>Message:${message}</h1>
<a href="j_spring_security_logout">logout</a>
</body>
</html>
文件都准备完成了,跑tomcat服务器之前先勾选好启动时需要的jar包。
OK,启动好服务器之后可以开始测试了。
Now,开始....
http://localhost:8080/springMVCSecurityDemo/ 这当然就是index.jsp页面了。
http://localhost:8080/springMVCSecurityDemo/user 当键入这个地址的时候,便会开始spring security的控制了,地址会变为 http://localhost:8080/springMVCSecurityDemo/login
输入错误的用户名或密码
输入正确后(当然你访问的是 http://localhost:8080/springMVCSecurityDemo/user,所以你的输入和数据库相对应的royal,royal.如果输入了admin,admin的话,那么它就进入默认的成功路径了,也就是下面这段代码声明的。)
<form-login login-page="/login"
authentication-failure-url="/login?error=true"
default-target-url="/user" />
默认的路径: default-target-url="/user" ,所以它进入的还是user.jsp
此时你便是ROLE_ADMIN管理员权限,所以你也可以进入admin.jsp的页面
OK,我们logout,注销出来用ROLE_USER身份登录试试。
http://localhost:8080/springMVCSecurityDemo/user
现在你键入访问地址访问admin.jsp
http://localhost:8080/springMVCSecurityDemo/admin
你会发现forbidden了,为什么?因为你现在不是ROLE_ADMIN身份了呗。
当然,还有“两周之内不必登录”remeber me的功能就自己去测试完了,没问题。
也好,就这样吧。