原创性声明:本文完全为笔者原创,请尊重笔者劳动力。转载务必注明原文地址。
Spring security
是Spring
框架家族中的一员,基于J2EE企业应用软件提供全面安全服务,归纳包含两部分内容: 认证
和 授权
,认证理解为登录,授权理解为权限控制,即什么用户能访问什么路径,请求什么资源。
个人认为,Spring Security
对于入门而言,学习曲线较高,网上也很难找到系统深入浅出的源码剖析。笔者在项目中很早就使用到了它,但一直处于拷贝、修改的level,最近专门花时间去探究一下它的核心源码,在探究源码之前,会先建立一个空项目以集成Spring Security
。
集成
spring security
的几种方式:
1.java配置的方式;
2.基于Spring Boot
;
3.xml配置的方式;
4.基于Spring MVC
本文基于第二种。so,首先将搭建一个空的Spring boot
项目, 可以参考我的另一篇文章(也可参考上面第2点的链接,更为详细)。现在假设已经有了一个空的spring boot
项目。
引入
在Spring Boot
项目中引入spring security
很简单,在pom.xml中添加依赖即可(暂时先注释,不启用):
<!--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
-->
如果单独引入
spring-security-web
和spring-security-config
等,需要特别注意和当前项目spring boot
版本兼容问题。
创建一个Controller
package com.mycompany.app.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Controller
public class MainController {
@RequestMapping("/")
public String root() {
return "redirect:/index";
}
@RequestMapping("/index")
public String index() {
return "index";
}
@RequestMapping("/user/index")
public String userIndex() {
return "user/index";
}
@RequestMapping("/login")
public String login() {
return "login";
}
@RequestMapping("/login-error")
public String loginError(Model model) {
model.addAttribute("loginError", true);
return "login";
}
}
里面定义了几个可供客户端访问的接口。
拓展:
@Controller
和@RestController
的区别:
1.@Controller
修饰的Controller类,其方法返回的字符串,会配合视图解析器InternalResourceViewResolver
匹配到对应的视图页面,如返回success
,则会对应返回success.html
或success.jsp
等。
2.而@RestController
则会返回success
字符串,而且是方法返回什么,页面就接收到什么。
当使用@Controller
时,需要方法返回JSON字符串,只需要再加上@RequestBody
即可。因此,可以认为:
@RestController
=@Controller
+@RequestBody
;
创建对应的视图
1.index.html:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello Spring Security</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<div th:substituteby="index::logout"></div>
<h1>This is a secured page!</h1>
<p><a href="/index" th:href="@{/index}">Back to home page</a></p>
</body>
</html>
2.login.html:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login page</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<h1>Login page</h1>
<p>Example user: user / password</p>
<p th:if="${loginError}" class="error">Wrong user or password</p>
<form th:action="@{/login}" method="post">
<label for="username">Username</label>:
<input type="text" id="username" name="username" autofocus="autofocus" /> <br />
<label for="password">Password</label>:
<input type="password" id="password" name="password" /> <br />
<input type="submit" value="Log in" />
</form>
<p><a href="/index" th:href="@{/index}">Back to home page</a></p>
</body>
</html>
3.user/index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello Spring Security</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<div th:substituteby="index::logout"></div>
<h1>This is a secured page!</h1>
<p><a href="/index" th:href="@{/index}">Back to home page</a></p>
</body>
</html>
4.main.css
body {
font-family: sans;
font-size: 1em;
}
p.error {
font-weight: bold;
color: red;
}
div.logout {
float: right;
}
为了方便,视图代码直接取自官方项目中的(可以点击这里,
spring-security-4.2.3.RELEASE\samples\boot\helloworld
),因为采用thymeleaf模板,因此引入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
此时,项目结构如下:
启动项目后访问localhost:8080
:
是我们想要的。然后再点击上图圈圈里的安全页面链接。发现也打开了:
显然不是我们想要的。我们希望应该跳转到登录页面login.html
的,登录之后才能访问这个安全页面/user/index
。此时再放开pom.xml中security依赖的注释。maven
重新下载依赖,重启项目,再访问localhost:8080
:
什么也没做,只是在spring boot
项目的pom.xml中通过spring-boot-starter-security
依赖了Spring security
。它就已经为我们拦截了所有的请求(默认是所有)。显然,我不造这个登录表单该填啥,也不希望首页请求都被拦截。那么是时候简单的进行一些自定义配置了。
创建SecurityConfig.java
,java化配置(也可以用xml配置替代,上面有链接)
SecurityConfig.java
是用于java式配置security。如下:
文件内容为:
package com.mycompany.app.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* Created by pengxg on 2017/9/19.
*/
@EnableWebSecurity // 1
public class SecurityConfig extends WebSecurityConfigurerAdapter { // 2
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() // 3
.antMatchers("/index", "/css/**").permitAll() //4
.antMatchers("/user/**").hasRole("USER") //5
.and() // 6
.formLogin()
.loginPage("/login").failureUrl("/login-error");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { // 7
auth
.inMemoryAuthentication() //8
.withUser("user").password("password").roles("USER"); // 9
}
}
再重启项目,访问下localhost:8080
:
点击安全页面链接,发现跳转到登录页面了:
输入user
和password
,点击登录。
这是预想的效果。至此,一个最最基本的Spring security
配置就已经完成,并且可以使用了。
下面说明下这个最基本的SecurityConfig.java
文件。
这个类继承自
WebSecurityConfigurerAdapter
抽象类。重写了一个方法protected void configure(HttpSecurity http)
。 此外,还通过@Autowired
注入了一个方法public void configureGlobal(AuthenticationManagerBuilder auth)
(方法名configureGlobal
无关紧要)。
1.注释//1: 启用该security
配置。没有这个注解,此文件等同于没有。
2.注释//2: 继承自WebSecurityConfigurerAdapter
。
3.注释//3:authorizeRequests()
启用基于HttpServletRequest
的限制访问,启用之后就可以对某些路径进行访问权限控制了。
4.注释//4和//5: 匹配路径为/index
或/css/**
的请求是不进行拦截的(permitAll()
),匹配/user/**
路径的请求是需要USER
角色的用户才可以访问。
5.注释//6及后面两行: 指定支持基于表单的身份验证(formLogin()
),指定登录的请求路径(loginPage('/login')
),以及登录失败的跳转路径failureUrl("/login-error")
。而这几个路径,在MainController
中均有定义,结合看起来就一目了然了。
6.注释//7: 一个依赖注入的方法,项目启动时Spring
会自动注入并实例化auth
,并执行configureGlobal()
方法。
7.注释//8: 启用内存验证,同时返回一个Configurer
(InMemoryUserDetailsManagerConfigurer
)用来自定义内存验证。
8.注释//9:在内存中创建一个用户(用户名为username
, 密码为password
,角色为USER
)。登录表单输入的用户名密码就是以此为准。
个人认为,启用内存验证一般不是实际开发中常用的,在数据库中对应有
user
用户表,查表数据进行验证才是企业级项目应用spring security
更为常见的打开方式(后面再拓展)。不过对于个人博客等单用户系统而言,内存登录验证还是可以的。
至此,这个项目就算是应用了spring security
,但是和它搭噶的自己的类就只有SecurityConfig.java
,显然,它很重要!这里面涉及到几个重要的来源于Spring Security
的类。它们是:
1.
WebSecurityConfigurerAdapter
2.HttpSecurity
3.AuthenticationManagerBuilder
文章内容太长了,不得不分一下。下篇尝试以这三个类为切入点,进入Spring Security
的源码探索一下。