Spring Security 是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。原名Acegi Security,最早于2003年起源于Spring社区。 2007年末正式归为Spring Framework的正式子项目,并改名为Spring Security 。此后, Spring Security有了长远发展,现已成为一款基于Spring Framework的广泛应用的安全框架,主要为应用服务提供用户认证(Authentication)和用户授权(Authorization)功能。详情可参考Spring Security 官方文档。
Spring Security 针对安全方面的两大难题, 鉴权(Authentication)和授权(Authorization)提供了灵活强大的解决方案。
Spring Security优势:
灵活性, Spring Security并不局限于Spring MVC,虽然它是基于Spring Framework实现的,但它并不依赖于Spring MVC,可以独立于MVC应用在其他Java EE框架之上。
功能强大,Spring Security的安全管制并不只限制于Web请求,除此之外它还可以针对方法调用通过AOP的方式进行安全管制,甚至可以对域对象实例(Domain Object Instance)进行访问控制。
安全保护, 防止伪造身份, Spring Security 会自动拦截站点所有状态变化的请求(非GET,HEAD,OPTIONS和TRACE的请求),防止跨站请求伪造(CSRF防护),即防止其他网站或是程序POST等请求本站点。
如果项目需要安全控制功能,不用自己去实现一套, 集成Spring Security专业安全框架是首选, 适用后台管理、接口资源管理、微服务统一鉴权等场景。
处理流程图:

客户端发起一个请求,进入 Security 过滤器链。
当到 LogoutFilter 的时候判断是否是登出路径,如果是登出路径则到 logoutHandler ,如果登出成功则到 logoutSuccessHandler 登出成功处理,如果登出失败则由 ExceptionTranslationFilter ;如果不是登出路径则直接进入下一个过滤器。
当到 UsernamePasswordAuthenticationFilter 的时候判断是否为登录路径,如果是,则进入该过滤器进行登录操作,如果登录失败则到 AuthenticationFailureHandler 登录失败处理器处理,如果登录成功则到 AuthenticationSuccessHandler 登录成功处理器处理,如果不是登录请求则不进入该过滤器。
当到 FilterSecurityInterceptor 的时候会拿到 uri ,根据 uri 去找对应的鉴权管理器,鉴权管理器做鉴权工作,鉴权成功则到 Controller 层否则到 AccessDeniedHandler 鉴权失败处理器处理。
投票机制, 三种表决方式, 默认采用一票制(AffirmativeBased)
| 类名 | 描述 |
|---|---|
| AffirmativeBased | 只要有一个投票器允许访问, 请求立刻允许放行, 不管之前存在拒绝的决定 |
| ConsensusBased | 多数票机制(允许或拒绝),多数的一方决定了AccessDecisionManager的结果。平局的投票和空票(全部弃权)的结果是可配置的 |
| UnanimousBased | 所有的投票器必须全部是允许的, 否则访问将被拒绝 |
Spring Boot 与Spring Security 集成, 包含一般集成用法, 还包括自定义用户登陆页面使用, 自定义内存模式验证, 以及自定义登陆成功与失败逻辑处理。
创建工程spring-boot-security-integrate

启动类:
com.mirson.spring.boot.security.integrate.startup.SecurityIntegrateApplication
@SpringBootApplication
@ComponentScan(basePackages = {"com.mirson"})
public class SecurityIntegrateApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityIntegrateApplication.class, args);
}
}
MAVEN依赖
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
dependencies>
定义一个外部访问接口
创建实体
定义一个用户实体
com.mirson.spring.boot.security.integrate.po.User
@Data
public class User {
/**
* ID
*/
private Integer id;
/**
* 用户名称
*/
private String name;
/**
* 年龄
*/
private String age;
/**
* 省份
*/
private String province;
/**
* 创建时间
*/
private Date createDate;
}
提供Web访问接口
提供一个获取用户信息接口
com.mirson.spring.boot.security.integrate.controller.UserController
@RestController
@RequestMapping("/user")
@Log4j2
public class UserController {
@GetMapping("/getUserInfo")
@ResponseBody
public User getUserInfo() {
User user = new User();
user.setId(0);
user.setAge("21");
user.setName("user1");
user.setCreateDate(new Date());
return user;
}
}
工程配置
server:
port: 22618
spring:
application:
name: security-integrate
# security 安全配置
security:
user:
name: "admin"
password: "admin"
设置默认的用户名与密码为admin。
功能验证
请求获取用户信息接口
访问接口: http://127.0.0.1:22618/getUserInfo
没有鉴权的情况下, 会出现登陆界面。

输入用户信息admin/admin, 再次请求获取用户信息接口

正确输入用户信息后, 可以正常访问用户信息接口。
Spring Security 内置会有一套登陆页面, 也可以自定修改, 这里通过freemark模板来实现自定登陆页面渲染。
application.yml增加配置:
# freemarker 模板配置
freemarker:
allow-request-override: false
allow-session-override: false
cache: true
charset: UTF-8
check-template-location: true
content-type: text/html
enabled: true
expose-request-attributes: false
expose-session-attributes: false
expose-spring-macro-helpers: true
prefer-file-system-access: true
suffix: .ftl
template-loader-path: classpath:/templates/
增加freemark模板文件:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>自定义系统登陆title>
<link href="/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/signin.css" rel="stylesheet">
head>
<body>
<div class="container form-margin-top">
<form class="form-signin" action="/user/doUserLogin" method="post">
<h2 class="form-signin-heading" align="center">自定义系统登陆h2>
<input type="text" name="username" class="form-control form-margin-top" placeholder="账号" required autofocus>
<input type="password" name="password" class="form-control" placeholder="密码" required>
<button class="btn btn-lg btn-primary btn-block" type="submit">sign inbutton>
form>
div>
<footer>
<p>support by: mirsonp>
footer>
body>
html>
添加CSS静态资源文件

添加JAVA CONFIG配置:
com.mirson.spring.boot.security.integrate.config.SpringSecurityConfiguration
@Configuration
@EnableWebSecurity
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/userLoginForm",
"/css/**").permitAll()
.antMatchers("/user/getUserInfo").authenticated()
.and()
.formLogin()
.loginPage("/user/userLoginForm") //自定义登录页面
.permitAll() //允许所有人访问该路由
.and()
.csrf()
.disable() //暂时禁用csrc否则无法提交
.httpBasic();
}
}
重启, 访问接口: http://127.0.0.1:22618/user/getUserInfo
会自动跳转到自定义的登陆页面:

修改com.mirson.spring.boot.security.integrate.config.SpringSecurityConfiguration配置:
@Configuration
@EnableWebSecurity
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/doUserLogin", "/user/userLoginForm",
"/css/**").permitAll()
.antMatchers("/user/getUserInfo").authenticated() //hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/user/userLoginForm") //自定义登录页面
.loginProcessingUrl("/user/doUserLogin") // 自定义登陆处理地址
.permitAll() //允许所有人访问该路由
.and()
.csrf()
.disable() //暂时禁用csrc否则无法提交
.httpBasic();
}
}
通过Spring Security 可以控制, 哪些资源需要受权限保护, 哪些可以开放访问。
开放访问
/user/userLoginForm 用户登陆页面
/user/doUserLogin 登陆处理地址
/css/** 静态资源文件
权限保护
/user/getUserInfo 获取用户信息接口
可以指定Role角色权限, 不指定, 只要登陆即拥有访问权限。
loginPage指定/user/userLoginForm为自定义登陆页面;
loginProcessingUrl为登陆请求处理接口地址,可以不用对其做具体实现,Spring Security 会做默认处理。
创建鉴权用户对象:
com.mirson.spring.boot.security.integrate.po.OAuthUser
@Data
public class OAuthUser extends User {
public OAuthUser(String account, String password){
super(account, password, true, true, true, true, Collections.EMPTY_SET);
}
}
继承的是Spring Security 的User对象, 将账号和密码信息, 传递至父类构造方法。
修改SpringSecurityConfiguration配置
增加:
@Configuration
@EnableWebSecurity
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* 用户认证服务
* */
@Bean
@Override
protected UserDetailsService userDetailsService() {
//创建基于内存用户管理对象
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//自定义权限
Collection<GrantedAuthority> adminAuth = new ArrayList<>();
adminAuth.add(new SimpleGrantedAuthority("ADMIN"));
//自定义用户
OAuthUser oAuthUser = new OAuthUser("admin", "admin123");
manager.createUser(oAuthUser);
return manager;
}
/**
* 配置密码编码器, 不需加密
* @return
*/
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
...
}
采用内存模式管理用户对象InMemoryUserDetailsManager, 自定义认证用户名和密码, 分别为admin,admin123。 需要配置密码编码器, 可以支持自定义密码加密方式, 这里不需加密, 配置NoOpPasswordEncoder。
Spring Security 提供了接口, 登陆成功, 可以通过处理器实现自定义逻辑。
新建com.mirson.spring.boot.security.integrate.handler.SecuritySuccessHandler
@Component
@Log4j2
public class SecuritySuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
/**
* 认证成功处理
* @param request
* @param response
* @param authentication
* @throws IOException
* @throws ServletException
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
log.info("Process in SecuritySuccessHandler ==> login success.");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication));
}
}
这里通过自定义登陆成功处理器, 将登陆成功的信息返回客户端。
将处理器加入到自定义配置SpringSecurityConfiguration中:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/doUserLogin", "/user/userLoginForm",
"/css/**").permitAll()
.antMatchers("/user/getUserInfo").authenticated() //hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/user/userLoginForm") //自定义登录页面
.loginProcessingUrl("/user/doUserLogin") // 自定义登陆处理地址
.successHandler(securitySuccessHandler) // 自定义登陆成功处理器
.permitAll() //允许所有人访问该路由
.and()
.csrf()
.disable() //暂时禁用csrc否则无法提交
.httpBasic();
}
如果登陆失败, 也可以通过处理器实现自定义逻辑。
新建com.mirson.spring.boot.security.integrate.handler.SecurityFailureHandler
@Component
@Log4j2
public class SecurityFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
log.info("Process in SecurityFailureHandler ==> login failure.");
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(exception.getMessage()));
}
}
将登陆失败的错误信息, 返回给客户端。
修改自定义配置SpringSecurityConfiguration:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/doUserLogin", "/user/userLoginForm",
"/css/**").permitAll()
.antMatchers("/user/getUserInfo").authenticated() //hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/user/userLoginForm") //自定义登录页面
.loginProcessingUrl("/user/doUserLogin") // 自定义登陆处理地址
.successHandler(securitySuccessHandler) // 自定义登陆成功处理器
.failureHandler(securityFailureHandler) // 自定义登陆失败处理器
.permitAll() //允许所有人访问该路由
.and()
.csrf()
.disable() //暂时禁用csrc否则无法提交
.httpBasic();
}
验证内存模式鉴权
内存模式我们设置的用户名和密码为admin/admin123
默认配置文件是admin/admin

输入admin/admin123, 成功登陆, 内存模式已生效。
登陆成功处理器验证
重启服务, 访问获取用户信息接口, http://127.0.0.1:22618/user/getUserInfo
默认是会跳转到登陆页面, 如果没配置登陆成功处理器, 登陆成功后, 会进入上一次访问页面

可以看到, 登陆成功后, 并没有跳转到上一次访问的用户信息接口, 而是返回了登陆成功处理器的结果。
登陆失败处理器验证
同样, 问获取用户信息接口, http://127.0.0.1:22618/user/getUserInfo, 会自动跳转到登陆页面。

采用错误的用户密码, 返回了登陆失败处理器的结果。
Spring Security 提供了鉴权与授权的功能支持, 这里做了详细讲解, 如何使用与配置, 并讲解了自定义鉴权处理功能, 实际业务当中,并非一层不变, 会做不同配置修改,比如自定义资源访问配置, 不同项目有不同的要求, 掌握这些自定义配置, 基本可以覆盖主要的业务场景, 针对更复杂的鉴权, 可以采用oauth2做鉴权处理, 在后续教程中会做讲解。
教程源码下载地址: https://download.csdn.net/download/hxx688/86400104