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
http://localhost:8080/springMVCSecurityDemo/admin
你会发现forbidden了,为什么?因为你现在不是ROLE_ADMIN身份了呗。
当然,还有“两周之内不必登录”remeber me的功能就自己去测试完了,没问题。
也好,就这样吧。