1.1 认证和授权概念
在实际生产环境中,系统资源不能随意访问,必须先确认用户身份,再分配操作权限,这就是认证 与授权要解决的问题。
- 认证:识别用户身份,验证用户名 / 密码、手机号验证码等,让系统知道 "你是谁"。
- 授权:认证通过后,指定用户可操作的功能、可访问的资源,让系统知道 "你能做什么"。
权限控制本质就是对用户完成认证 + 授权的全流程管理。
1.2 权限模块数据模型
实现权限控制需要5 / 7 张核心表支撑,角色表 t_role 处于核心位置,用户、权限、菜单均与角色为多对多关系。
涉及表结构:

- 用户表 t_user
- 权限表 t_permission
- 角色表 t_role
- 菜单表 t_menu
- 用户角色关系表 t_user_role
- 角色权限关系表 t_role_permission
- 角色菜单关系表 t_role_menu
表使用场景:
- 认证:仅需用户表 t_user,校验用户名 / 密码即可。
- 授权:需 7 张表联动,根据用户→角色→菜单 / 权限,确定用户可访问资源与操作权限。
1.3 Spring Security 简介
Spring Security 是 Spring 官方提供的强大、高度可定制的认证与授权框架,是 Spring 项目安全管控的事实标准。
核心优势:
- 完整支持认证、授权
- 防护会话固定、点击劫持、CSRF 攻击
- 与 Servlet API、Spring Web MVC 无缝集成
- 易扩展,满足自定义安全需求
Maven 依赖:
xml
<!--security启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
常用权限框架:Spring Security、Apache Shiro。
1.4 Spring Security 入门案例
1.4.1 工程搭建
xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>ICan_parent</artifactId>
<groupId>com.hg</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring_security</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
</project>
1.4.2 创建启动类
java
java
@SpringBootApplication
@EnableWebSecurity
public class SpringSecurityApp {
public static void main(String[] args) {
ConfigurableApplicationContext ac = SpringApplication.run(SpringSecurityApp.class, args);
}
}
1.4.3 启动测试
访问 http://localhost:8080,Spring Security 自动生成默认登录页面,需输入账号密码登录。

1.4.5 FilterChainProxy 核心过滤器
Spring Boot 启动时,会加载名为 springSecurityFilterChain 的过滤器 FilterChainProxy,所有请求先经过此过滤器,再分发到对应子过滤器处理。
@SpringBootApplication
@EnableWebSecurity//启动security
public class SpringSecurityApp {
public static void main(String[] args) {
ConfigurableApplicationContext ac = SpringApplication.run(SpringSecurityApp.class, args);
Object bean = ac.getBean("springSecurityFilterChain");
//输出class org.springframework.security.web.FilterChainProxy
System.out.println(bean.getClass());
}
}
点击进入FilterChainProxy的源码,执行过滤器时会调用这个类的doFiler方法:
再进入doFilterInternal方法里面打断点,观察它的具体的初始化流程:

1.4.6 Spring Security 15 个常用过滤器
- SecurityContextPersistenceFilter:初始化安全上下文,保存认证权限信息
- WebAsyncManagerIntegrationFilter:集成 Spring 异步机制
- HeaderWriterFilter:添加请求头安全信息
- CsrfFilter:防跨域请求伪造攻击
- LogoutFilter:处理退出登录,清除认证信息
- UsernamePasswordAuthenticationFilter:用户名密码认证核心过滤器
- DefaultLoginPageGeneratingFilter:生成默认登录页
- DefaultLogoutPageGeneratingFilter:生成默认退出页
- BasicAuthenticationFilter:解析 Basic 认证请求头
- RequestCacheAwareFilter:缓存请求对象
- SecurityContextHolderAwareRequestFilter:包装 Request,扩展 API
- AnonymousAuthenticationFilter:未登录时创建匿名身份
- SessionManagementFilter:限制同一用户会话数量
- ExceptionTranslationFilter:统一处理安全异常
- FilterSecurityInterceptor:权限校验核心过滤器
1.5 入门案例改进(适配生产环境)
原生入门案例存在 4 个问题:所有资源都需认证、需要自定义的登录页、明文配置账号、密码明文存储,需针对性优化。
1.5.1 配置可匿名访问资源
java
@Component
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
//配置认证信息来源
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
//忽略静态资源,匿名访问
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/pages/**");
}
//HTTP请求安全配置
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
}
效果:pages目录下的文件可以在没有认证的情况下任意访问
1.5.2 使用自定义登录页面
1.自定义 login.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h3>自定义登录页面</h3>
<form action="/login" method="post">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
2.安全配置优化
java
@Component
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/login.html");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//自定义登录页
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/pages/index.html",true); //登录成功,总是返回的页面
//权限配置
http.authorizeRequests()
.anyRequest().authenticated();
//关闭CSRF防护
http.csrf().disable();
}
}
效果: 使用http://localhost:8080/访问,此时就能访问自定义的登录页面。
1.6 从数据库查询用户信息
生产环境需从数据库动态加载用户,需实现 UserDetailsService 接口,框架自动调用完成认证。
java
@Service
public class UserServiceImpl implements UserDetailsService {
//模拟数据库用户数据
private static Map<String, UserInfo> mapSql = new HashMap<>();
static {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
mapSql.put("admin",new UserInfo("admin", passwordEncoder.encode("111")));
mapSql.put("test",new UserInfo("test",passwordEncoder.encode("222")));
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo userInfo = mapSql.get(username);
if (userInfo == null){
return null;
}
//明文密码{noop}
String password = "{noop}"+userInfo.getPassword();
//权限校验码
List<GrantedAuthority> list = new ArrayList<>();
list.add(new SimpleGrantedAuthority("add"));
list.add(new SimpleGrantedAuthority("delete"));
list.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
return new User(username, password, authorityArrayList);
}
}
UserInfo 用户实体类:
java
//用户实体
public class UserInfo {
String username;
String password;
public UserInfo(String username,String password){
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "UserInfo{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
配置认证管理器WebSecurityConfig.class :
@Autowired
private UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
1.7 密码加密(BCrypt)
明文密码不安全,采用 BCryptPasswordEncoder 加密,随机盐混入密文,匹配无需单独存盐。
加密特点 :同一密码每次加密结果不同,matches() 方法可正确匹配。
1.配置加密 Bean
WebSecurityConfig.class
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
2.WebSecurityConfig.class 中指定密码加密对象

3.修改UserService实现类
java
@Service
public class UserServiceImpl implements UserDetailsService {
//模拟向mysql数据库中插入数据
private static Map<String, UserInfo> mapSql = new HashMap<>();
static {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
mapSql.put("admin",new UserInfo("admin", passwordEncoder.encode("111")));
mapSql.put("test",new UserInfo("test",passwordEncoder.encode("222")));
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//模拟从数据库中查询用户
UserInfo userInfo = mapSql.get(username);
if (userInfo == null){
return null;
}
//模拟查询数据库中用户的密码 去掉明文标识{noop}
String password = userInfo.getPassword();
//权限校验码
List<GrantedAuthority> authorityArrayList = new ArrayList<>();
authorityArrayList.add(new SimpleGrantedAuthority("add"));
authorityArrayList.add(new SimpleGrantedAuthority("delete"));
authorityArrayList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
return new User(username, password, authorityArrayList);
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
1.8 配置多种权限校验规则
按权限 / 角色细粒度控制页面访问:
为了测试方便,首先在项目中创建a.html、b.html、c.html几个页面
WebSecurityConfig.class:
java
@Override
protected void configure(HttpSecurity http) throws Exception {
//自定义登录页
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/pages/index.html",true); //登录成功,总是返回的页面
//权限配置
http.authorizeRequests()
//a.html页面需add权限访问
.mvcMatchers("/pages/a.html").hasAuthority("add")
//b.html页面需delete权限访问
.mvcMatchers("/pages/b.html").hasAuthority("delete")
//c.html需ADMIN角色访问
.mvcMatchers("/pages/c.html").hasRole("ADMIN")
.anyRequest().authenticated(); //任意请求必须认证过的
//关闭跨站请求防护
http.csrf().disable();
}
为方便测试,改造UserServiceImpl
java
@Service
public class UserServiceImpl implements UserDetailsService {
private static Map<String, UserInfo> mapSql = new HashMap<>();
static {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
mapSql.put("admin",new UserInfo("admin", passwordEncoder.encode("111")));
mapSql.put("test",new UserInfo("test",passwordEncoder.encode("222")));
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo userInfo = mapSql.get(username);
if (userInfo == null){
return null;
}
//密码
String password = userInfo.getPassword();
//权限校验码
List<GrantedAuthority> authorityArrayList = new ArrayList<>();
if ("admin".equals(username)) {
authorityArrayList.add(new SimpleGrantedAuthority("add"));
}
if ("test".equals(username)) {
authorityArrayList.add(new SimpleGrantedAuthority("delete"));
}
if ("admin".equals(username)) {
authorityArrayList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
return new User(username, password, authorityArrayList);
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
此时分别用admin用户和test用户登录测试效果。
1.9 注解方式权限控制
更灵活的方法级权限控制,开启注解后直接在接口上标注。
1.开启注解支持
java
@Component
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启权限注解支持
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
}
2.Controller类权限注解
java
@RestController
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/add")
@PreAuthorize("hasAuthority('add')")//表示用户必须拥有add权限才能调用当前方法
public String add(){
System.out.println("add...");
return "success";
}
@RequestMapping("/delete")
@PreAuthorize("hasAuthority('delete')")//表示用户必须拥有delete权限 才能调用当前方法
public String delete(){
System.out.println("delete.....");
return "success";
}
@RequestMapping("/admin")
@PreAuthorize("hasRole('ROLE_ADMIN')")//表示用户必须拥有ROLE_ADMIN角色 才能调用当前方法
public String admin(){
System.out.println("admin.....");
return "success";
}
}
1.10 退出登录配置
请求 /logout 自动退出,清除认证状态,跳转至登录页。
@Override
protected void configure(HttpSecurity http) throws Exception {
//原有配置...
//退出登录配置
http.logout().logoutUrl("/logout").logoutSuccessUrl("/login.html");
}
总结
本文基于 Spring Security 完整实现了认证 + 授权权限控制:
- 理清认证、授权核心概念与 7 张表数据模型
- 快速搭建入门案例,掌握 FilterChainProxy 过滤器链
- 适配生产环境:匿名资源、自定义登录页、数据库查用户、BCrypt 加密
- 细粒度权限控制:URL 配置 + 方法注解两种方式
- 完成登录、退出全流程配置
可直接基于此方案,对接移动端短信登录、后台管理系统权限管控。