导语
互联网已经成为了我们生活一部分,但是安全问题频发,身份认证变得更为复杂。如何让这一部分更安全?如何让这一部分的身份验证变得更简单?下面将介绍Spring全家桶中的安全框架SpringSecurity快速入门及使用,来解决身份验证问题.
springsecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作.下面使用实现一个springsecurity案例
创建maven工程(war包) 导入pom依赖
<!--Spring和SpringSecurity依赖-->
<dependencies>
<!--springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
<!--SpringSecurity相关依赖-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!--数据连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.13</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>80</port>
<!-- http://127.0.0.1:{port}/{path} -->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
web.xml配置
<!-- 编码过滤器 -->
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--//End 编码过滤器 -->
<!--springsecurity过滤器,做资源权限的拦截和验证-->
<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>
<!-- SpringMVC -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
springmvc.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:p="http://www.springframework.org/schema/p"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 包扫描 -->
<context:component-scan base-package="com.shemuel.controller" />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
<!--引入SpringSecurity配置文件-->
<!--<import resource="spring-security.xml" />-->
<import resource="spring-security.xml"/>
</beans>
springsecurity.xml 配置(最终配置,后面会讲每个部分什么意思)
<?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: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-4.2.xsd ">
<!-- 公共资源取消授权拦截 -->
<!-- <http pattern="/images/**" security="none"/>
<http pattern="/register" security="none"/>
<http pattern="/loginout" security="none"/> -->
<http pattern="/login.html" security="none"/>
<http pattern="/login/fail.html?error" security="none"/>
<!--
auto-config表示自动引入springsecurity相关过滤器
user-expression属性表示是否使用 表达式
-->
<http auto-config="true" use-expressions="true">
<!--配置需要过滤哪些页面,所有带有user的请求都需要ROLE_ADMIN权限-->
<intercept-url pattern="/user/**" access="hasRole('ROLE_ADMIN')" />
<form-login login-page="/login.html"
default-target-url="/user/list.html"
authentication-failure-url="/login/fail.html?error"
username-parameter="username"
password-parameter="password"
always-use-default-target="true" />
<!--禁用CSRF-->
<csrf disabled="true"/>
</http>
<!--授权认证管理器-->
<authentication-manager>
<authentication-provider>
<!-- 引用已经配置的加密算法 -->
<password-encoder ref="encoder"></password-encoder>
<!-- 对指定数据库表里的用户进行授权认证 -->
<jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username, password, enabled from users where username=?" />
</authentication-provider>
</authentication-manager>
<!-- 数据库连接池 -->
<beans:bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<beans:property name="url" value="jdbc:mysql://127.0.0.1:3306/security?characterEncoding=utf8" />
<beans:property name="username" value="root" />
<beans:property name="password" value="root" />
<beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
<beans:property name="maxActive" value="10" />
<beans:property name="minIdle" value="5" />
</beans:bean>
<!-- 配置加密算法 -->
<beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="9" /><!-- 加密的密码长度9 -->
</beans:bean>
</beans:beans>
创建UserController
创建用户管理的Controller,并加入用户列表功能
package cn.itcast.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping(value = "/admin/user")
public class UserController {
/***
* 用户管理
* @return
*/
@RequestMapping(value = "/list")
public String list(){
return "user_list";
}
}
在/WEB-INF/view/下创建user_list.jsp
为了演示效果,我们这里整点假数据展示
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>欢迎来到SpringSecurity的世界</title>
</head>
<body>
欢迎来到SpringSecurity的世界!<br />
<table>
<tr>
<td>用户ID</td>
<td>姓名</td>
<td>年龄</td>
</tr>
<tr>
<td>1</td>
<td>张三</td>
<td>王五</td>
</tr>
<tr>
<td>2</td>
<td>张三2</td>
<td>王五</td>
</tr>
<tr>
<td>3</td>
<td>张三3</td>
<td>王五</td>
</tr>
<tr>
<td>4</td>
<td>张三4</td>
<td>王五</td>
</tr>
</table>
</body>
</html>
实现对/user开始的请求拦截
在spring-security.xml中配置拦截信息和授权认证管理器
<http auto-config="true" use-expressions="true">
<!--所有带有admin的请求都需要ROLE_ADMIN权限-->
<intercept-url pattern="/user/**" access="hasRole('ROLE_ADMIN')" />
</http>
<!--授权认证管理器-->
<authentication-manager>
</authentication-manager>
http结点主要配置要拦截的url相关权限规则和处理方案。
auto-config =true:默认会配置多个SpringSecurity相关过滤器,如果不配,就不能正常使用SpringSecurity相关功能。
use-expressions:是否使用SpELl表达式。
,pattern表示要拦截的路径,可以用通配符表示, * * 表示所有路径。 access表示对应地址访问所需的权限,如果use-expressions="false"access="hasRole(‘ROLE_ADMIN’)"这里的hasRole就可以去掉,我们后面都会设置成false,直接去掉这里的hasRole方便一点。ROLE_ADMIN表示ADMIN角色,这列角色自定义,可以随意定义什么角色,不过注意,这里必须得大写。
发布测试
用tomcat发布测试,端口号根据你本机情况开放,我这里端口是80,可以省略
访问 localhost/user/list.html的时候跳转到了一个登陆界面,说明拦截配置生效了。
添加授权用户
接着我们为上面登陆那里添加授权用于,允许他们登陆。修改spring-security.xml,在authentication-manager结点下加入如下代码:
<authentication-provider>
<!--硬编码方式提供账号密码-->
<user-service>
<user name="admin" authorities="ROLE_ADMIN" password="123456" disabled="false" />
</user-service>
</authentication-provider>
这里提供了用户名为admin 密码123456 角色为ROLE_ADMIN的用户,这里的角色必须和上面http里配置的角色保持一致,否则仍然五权限访问。disabled=false表示不禁用也就是启用。这时候我们就可以通过该账号登录访问了。
登录后我们就可以访问http://localhost/user/list.html了
Spring Security常用配置
基于上面的案例我们继续学习SpringSecurity相关知识。
取消安全校验
我们网站中常常会有一些静态资源或者不需要校验权限的地址,例如注册和登录,我们在webapp下创建一个images文件夹,在里面放一张图片1.png。像这些地址或者静态资源我们如何取消权限校验呢?在spring-security.xml中加入如下代码:
<!--不需要过滤的静态资源和开放连接-->
<http pattern="/images/**" security="none" />
<http pattern="/login.shtml" security="none" />
<http pattern="/login/fail.shtml" security="none" />
这时候 http://localhost/images/1.jpg 就可以访问了。
自定义登录页面
刚才那个登录页面不太美观,我们能不能让登录地址跳转到指定页面呢?
首先我们创建一个登录页面
/WEB-INF/view/login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>登录</title></head>
<body>
欢迎登录!我自己的登录页面,后期可进行美化.
<form name='f' action='/login' method='POST'>
<table>
<tr>
<td>用户名:</td>
<td><input type='text' name='username' value=''></td>
</tr>
<tr>
<td>密码:</td>
<td><input type='password' name='password'/></td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit" value="Login"/></td>
</tr>
</table>
</form>
</body>
</html>
LoginController
package cn.itcast.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
/***
* 登录
* @return
*/
@RequestMapping(value = "/login")
public String login(){
return "login";
}
}
修改spring-security.xml 在http结点中加入如下代码:
<!--自定义登录-->
<form-login login-page="/login.shtml"
default-target-url="/user/list.shtml"
authentication-failure-url="/login/fail.html?error"
username-parameter="username"
password-parameter="password"
always-use-default-target="true" />
- login-page:自定义登录页url,默认为/login
- default-target-url:默认登录成功后跳转的url
- authentication-failure-url: 登录失败后跳转的url
- username-parameter:用户名的请求字段 默认为userName
- password-parameter:密码的请求字段 默认为password
- always-use-default-target:是否始终使用默认的地址,即登录成功后是否总是跳转到默认地址
再次登录,发现报403错误(如下),是因为SpringSecurity这里做了防csrf攻击校验,我们禁用csrf校验即可。
HTTP Status 403 – Forbidden
Type Status Report
Message Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
Description The server understood the request but refuses to authorize it.
Apache Tomcat/8.5.16
在http结点加入
<!--禁用CSRF-->
<csrf disabled="true" />
登录错误信息处理
在LoginController中加一个错误处理方法
/***
* 登录失败
* @return
*/
@RequestMapping(value = "/login/fail")
public String loginfail(@RequestParam(value = "error",required = false)String error, Model model){
if(error!=null){
model.addAttribute("msg","账号或者密码不对!");
}
return "login";
}
在登录的jsp回显错误
<tr>
<td colspan='2'>
${msg}<br />
<input name="submit" type="submit" value="Login"/>
</td>
</tr>
数据库中的用户账号密码登录
前面我们一直是写死的用户账号和密码,而真实环境中基本都是从数据库获取账号密码,这个如何实现?
首先创建一个数据库叫springsecurity,接着创建两张表,一张是users表,存放用户信息,另一张是authorities表,存放用户角色信息。
CREATE TABLE `users` (
`username` varchar(50) NOT NULL,
`password` varchar(60) NOT NULL,
`enabled` varchar(50) NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO users(username,password,enabled)VALUES('itcast','123456','true');
CREATE TABLE `authorities` (
`username` varchar(50) NOT NULL,
`authority` varchar(50) NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO authorities(username,authority)VALUES('itcast','ROLE_ADMIN');
然后加入在springsecurity.xml中加数据库连接池配置,上面的最终版配置文件已经有了我这里就不在写了. 如果配置了加密,则数据库里的密码也应该为加密的密码,否则登录失败.
最后登录成功就能看到如下页面了
参考:传智播客教学视频.
传智博客官网:http://itcast.cn