SpringBoot整合SpringSecurity实现一个简单的认证与授权应用

devtools/2024/11/24 7:04:05/

1、SpringSecurity 的简介

Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架,它是 Spring 项目组中用来提供安全认证服务的框架,能够为基于 Sprin g的企业应用系统提供声明式的安全访问控制解决方案。

Spring Security 的前身是 Acegi Security。它是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。Spring Security 采用了 AOP(面向切面编程)思想,并基于 Servlet 过滤器实现。

下面将介绍 Spring Boot 整合 Spring Security 实现一个简单的认证与授权应用,执行结果如下如:

(1)登录页面

(2)登录成功后,跳转至首页:

2、数据库准备

使用 MySQL 数据库,设计一个自定义的数据表结构,并添加数据。

-- 创建数据库
CREATE DATABASE IF NOT EXISTS db_admin; USE db_admin;-- 创建自定义的用户信息表
DROP TABLE IF EXISTS user_info;CREATE TABLE user_info(id BIGINT(20) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',username VARCHAR(50) NOT NULL COMMENT '用户名称',PASSWORD VARCHAR(60) COMMENT '用户密码',ENABLE TINYINT(4) NOT NULL DEFAULT 1 COMMENT '是否启用',roles VARCHAR(100) COMMENT '用户角色,多个角色之间用英文逗号分割',KEY key_username (username)
);-- 插入数据
INSERT INTO user_info(username,PASSWORD,ENABLE,roles) VALUES('admin','123456',1,'ROLE_ADMIN,ROLE_USER');
INSERT INTO user_info(username,PASSWORD,ENABLE,roles) VALUES('user','123456',1,'ROLE_USER');
INSERT INTO user_info(username,PASSWORD,ENABLE,roles) VALUES('panjunbiao','123456',1,'ROLE_USER');-- 查询
SELECT * FROM user_info;

3、创建项目

【示例】SpringBoot 整合 SpringSecurity 创建一个简单的认证应用。

3.1 创建 Spring Boot 项目

创建 SpringBoot 项目,项目结构如下图:

3.1 添加 Maven 依赖

在 pom.xml 配置文件中添加 Spring Security、MyBatis、JDBC、Thymeleaf 模板引擎、Lombok 依赖。

<!-- Spring Security 依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><version>2.7.18</version>
</dependency><!-- MyBatis 与 Spring Boot 整合依赖 -->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version>
</dependency><!-- MySQL 的 JDBC 数据库驱动 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency><!-- Lombok 依赖 -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency><!-- 引入Thymeleaf模板引擎 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

3.3 配置数据库连接参数

在项目的 application.yml 文件中,添加数据库连接的配置。

spring:# 使用Thymeleaf模板引擎thymeleaf:mode: HTML5encoding: UTF-8cache: falseservlet:content-type: text/html# 数据库连接datasource:url: jdbc:mysql://localhost:3306/db_admin?useSSL=false&amp&serverTimezone=UTCusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver

4、整合 MyBatis 框架实现持久化

4.1 创建实体类(Entity 层

在项目的 entity 层,创建 UserInfo 类(用户信息实体类),并继承 UserDetails 类。

package com.pjb.securitydemo.entity;import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;
import java.util.List;/*** 用户信息实体类*/
@Data
public class UserInfo implements UserDetails
{private Long id; //主键IDprivate String username; //用户名称private String password; //用户密码private String roles; //用户角色private boolean enable; //是否启用private List<GrantedAuthority> authoritys; //权限集合@Overridepublic Collection<? extends GrantedAuthority> getAuthorities(){return this.authoritys;}public void setAuthoritys(List<GrantedAuthority> authoritys){this.authoritys = authoritys;}@Overridepublic boolean isAccountNonExpired(){return true;}@Overridepublic boolean isAccountNonLocked(){return true;}@Overridepublic boolean isCredentialsNonExpired(){return true;}@Overridepublic boolean isEnabled(){return true;}
}

实现 UserDetails 定义的几个方法:

  • isAccountNonExpired、isAccountNonLocked 和 isCredentialsNonExpired 暂且用不到,统一返回 true,否则 Spring Security 会认为账号异常。
  • isEnabled 对应 enable 字段,将其代入即可。
  • getAuthorities 方法本身对应的是 roles 字段,但由于机构不一致,所以此次新建一个,并在后续进行填充。

4.2 Mapper动态代理接口(Mapper层)

在 Mapper层,创建 UserInfoMapper 类(用户信息Mapper动态代理接口)

package com.pjb.securitydemo.mapper;import com.pjb.securitydemo.entity.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;/*** 用户信息Mapper动态代理接口**/
@Repository
@Mapper
public interface UserInfoMapper
{@Select("SELECT * FROM user_info WHERE username=#{username}")UserInfo findUserName(@Param("username") String username);
}

5、整合 Spring Security 框架实现认证与授权

5.1 服务类(Service  层)

创建 LoginService 类(登录服务类),实现 UserDetailsService 接口,重写 loadUserByUsername 方法,实现登录认证功能。

package com.pjb.securitydemo.service;import com.pjb.securitydemo.entity.UserInfo;
import com.pjb.securitydemo.mapper.UserInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import java.util.List;/*** 登录服务类*/
@Service
public class LoginService implements UserDetailsService
{@Autowiredprivate UserInfoMapper userInfoMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{//获取用户信息UserInfo userInfo = userInfoMapper.findUserName(username);//用户不存在,抛出异常if (userInfo == null){throw new UsernameNotFoundException("用户不存在");}//将数据库形式的 roles 解析为 UserDetails 的权限集// AuthorityUtils.commaSeparatedStringToAuthorityList 方法是 Spring Security//提供的,该方法用于将逗号分割的权限集字符串切割成可用权限集对象列表List<GrantedAuthority> grantedAuthorityList = AuthorityUtils.commaSeparatedStringToAuthorityList(userInfo.getRoles());userInfo.setAuthoritys(grantedAuthorityList);return userInfo;}
}

5.2 处理类(Handler 层)

(1)登录成功处理类

package com.pjb.securitydemo.handler;import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** 登录成功处理类*/
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler
{@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException{//重定向至首页httpServletResponse.sendRedirect("/");}
}

(2)登录失败处理类

package com.pjb.securitydemo.handler;import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;/*** 登录失败处理类*/
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler
{@Overridepublic void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException authenticationException) throws IOException, ServletException{httpServletResponse.setContentType("application/json;charset=utf-8");PrintWriter out = httpServletResponse.getWriter();out.write("登录失败");}
}

 (3)403无权限处理类

package com.pjb.securitydemo.handler;import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;/*** 403无权限处理类*/
@Component
public class PermissionDeniedHandler implements AccessDeniedHandler
{@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException{httpServletResponse.setContentType("application/json;charset=utf-8");PrintWriter out = httpServletResponse.getWriter();out.write("403无权限");}
}

5.3 配置类(Config 层)

创建 WebSecurityConfig 类(Spring Security 配置类),并添加 @EnableWebSecurity 注解和继承 WebSecurityConfigurerAdapter 类。

package com.pjb.securitydemo.config;import com.pjb.securitydemo.handler.LoginFailureHandler;
import com.pjb.securitydemo.handler.LoginSuccessHandler;
import com.pjb.securitydemo.handler.PermissionDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;/*** Spring Security 配置类* @author pan_junbiao**/
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{@Autowiredprivate LoginSuccessHandler loginSuccessHandler;@Autowiredprivate LoginFailureHandler loginFailureHandler;@Autowiredprivate PermissionDeniedHandler permissionDeniedHandler;@Overrideprotected void configure(HttpSecurity http) throws Exception{http.authorizeRequests() //返回一个URL拦截注册器.antMatchers("/admin/api/**").hasRole("ADMIN") //设置授权角色.antMatchers("/user/api/**").hasRole("USER") //设置授权角色.antMatchers("/app/api/**", "/captcha.jpg").permitAll() //公开其权限.anyRequest() //匹配所有的请求.authenticated() //所有匹配的URL都需要被认证才能访问.and() //结束当前标签,让上下文回到 HttpSecurity.formLogin() //启动表单认证.loginPage("/myLogin.html") //自定义登录页面.loginProcessingUrl("/auth/form") //指定处理登录请求路径.permitAll() //使登录页面不设限访问//.defaultSuccessUrl("/index") //登录认证成功后的跳转页面.successHandler(loginSuccessHandler) //指定登录成功时的处理.failureHandler(loginFailureHandler) //指定登录失败时的处理.and().exceptionHandling().accessDeniedHandler(permissionDeniedHandler) //403无权时的返回操作.and().csrf().disable(); //关闭CSRF的防御功能}/*** 由于5.x版本之后默认启用了委派密码编译器,* 因而按照以往的方式设置内存密码将会读取异常,* 所以需要暂时将密码编码器设置为 NoOpPasswordEncoder*/@Beanpublic PasswordEncoder passwordEncoder(){return NoOpPasswordEncoder.getInstance();}
}

6、前端页面

6.1 控制器层(Controller层)

创建 IndexController 类(首页控制器),实现获取当前登录用户名并跳转至首页。

package com.pjb.securitydemo.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;
import java.security.Principal;/*** 首页控制器* @author pan_junbiao**/
@Controller
public class IndexController
{/*** 首页*/@RequestMapping("/")public String index(HttpServletRequest request){//获取当前登录人String userName = "未登录";Principal principal = request.getUserPrincipal();if(principal!=null){userName = principal.getName();}//返回页面request.setAttribute("userName",userName);return "/index.html";}}

6.2 编写登录页面

在 resources\static 静态资源目录下,创建 myLogin.html 页面。

注意:myLogin.html 页面必须放在 resources\static 静态资源目录下,否则页面无法加载。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登录</title><meta name="author" content="pan_junbiao的博客">
</head>
<body>
<form name="myForm" action="/auth/form" method="post"><table align="center"><caption>用户登录</caption><tr><td>登录账户:</td><td><input type="text" name="username" placeholder="请输入登录账户" value="panjunbiao" /></td></tr><tr><td>登录密码:</td><td><input type="password" name="password" placeholder="请输入登录密码" value="123456" /></td></tr><!-- 以下是提交、取消按钮 --><tr><td colspan="2" style="text-align: center; padding: 5px;"><input type="submit" value="提交" /><input type="reset" value="重置" /></td></tr></table>
</form>
</body>
</html>

6.3 编写首页

在 resources\templates 资源目录下,创建 index.html 页面。

注意:首页 index.html 页面中使用 Thymeleaf 模板 。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>首页</title><meta name="author" content="pan_junbiao的博客">
</head>
<body><h1 style="color: red">Hello,Spring Security</h1><p>博客信息:您好,欢迎访问 pan_junbiao的博客</p><p>博客地址:https://blog.csdn.net/pan_junbiao</p><p th:text="'当前登录人:' + ${userName}"></p><a href="/logout" onclick="return confirm('确认注销吗?');">登出</a>
</body>
</html>

7、运行项目

7.1 登录页面

7.2 登录成功后,跳转至首页


http://www.ppmy.cn/devtools/136487.html

相关文章

机器学习实战:银行客户是否认购定期存款

项目结构与步骤 1. 项目概述 项目名称&#xff1a;葡萄牙银行电话营销活动分析与定期存款认购预测目标&#xff1a;通过分析银行的电话营销数据&#xff0c;构建模型预测客户是否会认购定期存款。数据来源&#xff1a;葡萄牙银行营销活动数据集关键挑战&#xff1a;数据不平衡…

YOLOv8-ultralytics-8.2.103部分代码阅读笔记-utils.py

utils.py ultralytics\nn\modules\utils.py 目录 utils.py 1.所需的库和模块 2.def _get_clones(module, n): 3.def bias_init_with_prob(prior_prob0.01): 4.def linear_init(module): 5.def inverse_sigmoid(x, eps1e-5): 6.def multi_scale_deformable_attn_py…

vscode下面python调试报错ImportError: cannot import name ‘Literal‘ from ‘typing‘

1 问题描述 我在vscode下面编写python程序&#xff0c;这个程序是在一个英伟达anoconda环境下的项目。之前能运行能调试&#xff0c;最近发现只能运行ctlf5&#xff0c;但是使用f5进行调试时&#xff0c;报错“File “c:\Users\86137.vscode\extensions\ms-python.debugpy-202…

详解八大排序(五)------(计数排序,时间复杂度)

文章目录 1. 计数排序&#xff08;CountSort&#xff09;1.1 核心思路1.2 实现代码 2. 时间复杂度比较 1. 计数排序&#xff08;CountSort&#xff09; 1.1 核心思路 计数排序的核心思路是另外创建一个数组&#xff0c;记录原数组中出现的成员个数&#xff0c;再依次打印新数组…

SpringSecurity基于内存的多个登录用户支持

Spring Security 支持各种来源的用户数据&#xff0c;包括内存、数据库、LDAP 等&#xff0c;它们被抽象为一个 UserDetailsService 接口&#xff0c;任何实现了 UserDetailsService 接口的对象都可以作为认证数据源。在这种设计模式下&#xff0c;Spring Security 显得尤为灵活…

Android Activity 基础接口知识和常见问题

Activity 知识点及问题点 接口onMultiWindowModeChangedonConfigurationChanged 常见问题Android解决点击桌面图标&#xff0c;就重新启动应用程序问题 接口 onMultiWindowModeChanged 定义 onMultiWindowModeChanged是Android中Activity类的一个回调方法。它会在活动&#xf…

挂壁式空气净化器什么牌子净化好?测评高热度品牌排行

近年来&#xff0c;挂壁式空气净化器日益成为消费者关注的焦点。随着市场需求的激增&#xff0c;其品牌和型号亦愈发丰富。作为家电测评领域的专业人士&#xff0c;我已评测了众多挂壁式空气净化器&#xff0c;发现部分产品存在质量问题&#xff0c;净化效果不佳&#xff0c;尤…

第二十九章 TCP 客户端 服务器通信 - 记录的拼接

文章目录 第二十九章 TCP 客户端 服务器通信 - 记录的拼接记录的拼接多路复用 TCP设备正在关闭连接使用CLOSE命令断开连接 第二十九章 TCP 客户端 服务器通信 - 记录的拼接 记录的拼接 在某些情况下&#xff0c;TCP会将不同的记录连接在一起形成单个记录。如果客户端或服务器…