SpringBoot整合SpringSecurity

什么是SpringSecurity?

Spring Security是Spring提供的一套web的应用安全性的完整解决方案。

SpringSecurity采用责任式链的设计模式,它的核心是一组过滤器链。

主要包括:

  • 认证(Authentication):什么是认证?简单的说就是检验某个用户是否为系统合法用户,需要用户提供用户名和密码进行校验当前用户能否访问系统。
  • 授权(Authorization):就是当前用户?是否具有某种权限来进行某种操作。

SpringSecurity的认证流程

  1. 用户在浏览器中访问一个需要认证的URL。
  2. Spring Security会检查用户是否已经被认证(即是否已登录)。如果用户已经认证,那么他们可以正常访问该URL。如果用户未被认证,那么他们将被重定向到loginPage()对应的URL,通常是登录页面。
  3. 用户在登录页面输入用户名和密码,然后点击登录按钮,发起登录请求。
  4. 如果请求的URL和loginProcessingUrl()一致,那么Spring Security将开始执行登录流程。否则,用户可能需要重新进行认证。
  5. 在执行登录流程时,首先会经过UsernamePasswordAuthenticationFilter过滤器,该过滤器会取出用户输入的用户名和密码,然后将其放入一个容器(UsernamePasswordAuthenticationToken)中。这个容器会被用于后续的认证流程。
  6. 接下来,UsernamePasswordAuthenticationToken会被提交给AuthenticationManager进行管理。AuthenticationManager会委托给AuthenticationProvider进行具体的认证操作。在这个过程中,可能会涉及到密码的加密和比对。
  7. 如果认证成功,那么用户的认证信息将被存储在session中,并且用户可以正常访问他们之前试图访问的URL。如果认证失败,那么用户可能会被重定向到失败URL(如果配置了的话),或者可能会看到一个错误页面

代码示例

简单案例

1,引入maven依赖

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>

2,启动服务

3,访问 http://localhost:8080/ 会自动跳转进入登录页面 默认用户是 user  密码为服务后端启动显示的安全密码

4,登录成功访问我们提供的接口

5,退出登录 访问 http://localhost:8080/logout

完整案例

1,引入maven依赖

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>

2,User类

package com.security.domain;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;/*** @Program: SpringBoot* @ClassName User* @Author: liutao* @Description: 用户类* @Create: 2023-06-11 17:22* @Version 1.0**/@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("sys_user")
public class User implements Serializable {private static final long serialVersionUID = 1L;@TableId(type = IdType.AUTO)@ApiModelProperty("用户id")private Integer id;@ApiModelProperty("用户名")private String username;@ApiModelProperty("密码")private String password;@ApiModelProperty("性别")private int sex;@ApiModelProperty("年龄")private int age;}

3,实现UserDetails接口并重写所有方法

package com.security.domain;import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;/*** @Program: SpringBoot* @ClassName LoginUser* @Author: liutao* @Description: 登录用户* @Create: 2023-06-11 18:17* @Version 1.0**/@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {private User user;private List<String> permissions;@JSONField(serialize = false)private List<GrantedAuthority> authorities;public LoginUser(User user, List<String> permissions) {this.user = user;this.permissions = permissions;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if (authorities != null) {return authorities;}
//        authorities = new ArrayList<GrantedAuthority>();
//        // 封装权限信息 -》GrantedAuthority
//        permissions.forEach(s -> {
//            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(s);
//            authorities.add(simpleGrantedAuthority);
//        });authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());return authorities;}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

4,创建业务接口实现 UserDetailService接口 重写loadUserByUser(String s) 完成业务认证和授权

package com.security.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.security.domain.LoginUser;
import com.security.domain.Role;
import com.security.domain.User;
import com.security.domain.UserRole;
import com.security.mapper.RoleMapper;
import com.security.mapper.UserMapper;
import com.security.mapper.UserRoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
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.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;/*** @Program: SpringBoot* @ClassName service* @Author: liutao* @Description: 用户业务接口* @Create: 2023-06-11 17:43* @Version 1.0**/@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper mapper;@Autowiredprivate UserRoleMapper userRoleMapper;@Autowiredprivate RoleMapper roleMapper;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();lambdaQueryWrapper.eq(User::getUsername, s);User user = mapper.selectOne(lambdaQueryWrapper);if (Objects.isNull(user)) {throw new UsernameNotFoundException("用户不存在");}//TODO  查询对应角色和权限信息 注意角色前一定要以“ROLE_”开头
//        List<String> auths = new ArrayList<String>(Arrays.asList("ROLE_USER","sys:user"));List<String> auths = new ArrayList<String>(Collections.emptyList());// 通过用户id获取当前用户所有角色idList<Integer> ids = userRoleMapper.selectList(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, user.getId())).stream().map(UserRole::getRoleId).collect(Collectors.toList());// 获取角色List<String> roles = roleMapper.selectBatchIds(ids).stream().map(Role::getFlag).collect(Collectors.toList());auths.addAll(roles);auths.add("sys:user");return new LoginUser(user, auths);}}

6,配置核心配置文件

package com.security.config;import com.security.filter.JwtAuthenticationTokenFilter;
import com.security.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/*** @Program: SpringBoot* @ClassName SecurityConfig* @Author: liutao* @Description: security配置类* @Create: 2023-06-11 17:39* @Version 1.0**/@Configuration
@EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsServiceImpl userDetailsService;@Autowiredprivate PasswordEncoder passwordEncoder;@Autowiredprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {// 自定义用户认证
//        auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");// 数据库用户认证auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);}/*** anyRequest          |   匹配所有请求路径* access              |   SpringEl表达式结果为true时可以访问* anonymous           |   匿名可以访问* denyAll             |   用户不能访问* fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)* hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问* hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问* hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问* hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问* hasRole             |   如果有参数,参数表示角色,则其角色可以访问* permitAll           |   用户可以任意访问* rememberMe          |   允许通过remember-me登录的用户访问* authenticated       |   用户登录后可访问*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http// CSRF禁用,因为不使用session.csrf().disable()// 禁用HTTP响应标头.headers().cacheControl().disable().and()// 过滤请求.authorizeRequests()// 对于登录toLogin 允许匿名访问.mvcMatchers("/toLogin").permitAll().mvcMatchers("/css/**", "/js/**", "/swagger-resources/**", "/webjars/**", "/v2/**", "/doc.html", "/img/**", "/favicon.ico").permitAll()
//                .antMatchers("/**")
//                .hasAnyRole("USER")// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();
//                .and()
//                .formLogin()
//                .loginProcessingUrl("/toLogin")
//                .successForwardUrl("/home")
//                .and()
//                .httpBasic();http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);}@Bean@Overrideprotected AuthenticationManager authenticationManager() throws Exception {return super.authenticationManager();}@Beanpublic BCryptPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

7,抽离web接口代码

package com.security.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.security.common.Result;
import com.security.domain.LoginUser;
import com.security.domain.User;
import com.security.domain.dto.UserDto;
import com.security.domain.vo.UserVo;
import com.security.mapper.UserMapper;
import com.security.service.UserService;
import com.security.utils.JwtUtil;
import com.security.utils.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;import java.util.Objects;
import java.util.concurrent.TimeUnit;/*** @Program: SpringBoot* @ClassName UserServiceImpl* @Author: liutao* @Description:* @Create: 2023-06-12 00:51* @Version 1.0**/@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);@Autowiredprivate UserMapper mapper;@Autowiredprivate RedisUtil redisUtil;@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate BCryptPasswordEncoder passwordEncoder;@Overridepublic UserVo login(UserDto userDto) {UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDto.getUsername(), userDto.getPassword());Authentication authentication = authenticationManager.authenticate(token);if (Objects.isNull(authentication)) {throw new RuntimeException("登录失败!");}LoginUser loginUser = (LoginUser) authentication.getPrincipal();if (!passwordEncoder.matches(userDto.getPassword(), loginUser.getPassword())) {throw new RuntimeException("密码错误");}UserVo userVo = new UserVo();BeanUtils.copyProperties(loginUser.getUser(), userVo);String jwtToken = JwtUtil.getToken(userVo.getId().toString());String redisKey = "login:" + userVo.getId().toString();log.info("当前登录用户:{}",loginUser);redisUtil.set(redisKey, loginUser,20L, TimeUnit.MINUTES);userVo.setToken(jwtToken);return  userVo;}
}

8,web接口

package com.security.controller;import com.security.common.Result;
import com.security.domain.dto.UserDto;
import com.security.domain.vo.UserVo;
import com.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;/*** @author TAOGE*/
@RestController
public class BasicController {@Autowiredprivate UserService userService;@PostMapping("/toLogin")public Result<UserVo> login(@RequestBody UserDto userDto) {return  Result.success("登录成功", userService.login(userDto));}@PreAuthorize("hasRole('ADMIN')")@GetMapping("/hasRole")public Result<String> hasRole() {return Result.success("ADMIN");}@PreAuthorize("hasAuthority('sys:user')")@GetMapping("/hasPerm")public Result<String> hasPerm() {return Result.success("sys:user");}
}

测试

1,启动服务

2,访问 /hasRole接口

3,认证授权

4,携带token访问 /hasRole 和 /hasPerm接口

结尾

到这里我们今天的SpringBoot整合和尝鲜SpringSecurity就结束了!!

欢迎双击点赞 666!!!

everybody see you!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/505258.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【递归】【回溯】Leetcode 112. 路径总和 113. 路径总和 II

【递归】【回溯】Leetcode 112. 路径总和 113. 路径总和 II 112. 路径总和解法&#xff1a;递归 有递归就有回溯 记得return正确的返回上去 113. 路径总和 II解法 递归 如果需要搜索整棵二叉树&#xff0c;那么递归函数就不要返回值 如果要搜索其中一条符合条件的路径&#xff…

vue中将某个不太规则的json转成对象,或者将对象转成json字符串

vue中将某个不太规则的json转成对象&#xff0c;或者将对象转成json字符串 以我自己做的项目某个不规则的json为例 将json对象转成json字符串&#xff1a; JSON.stringify(jsonData); 将不规则json字符串转成对象并获取对应的属性的值&#xff1a; JSON.parse(jsonData).Name…

详细了解C++中的namespace命名空间

键盘敲烂&#xff0c;月薪过万&#xff0c;同学们&#xff0c;加油呀&#xff01; 目录 键盘敲烂&#xff0c;月薪过万&#xff0c;同学们&#xff0c;加油呀&#xff01; 一、命名空间的理解 二、&#xff1a;&#xff1a;作用域运算符 三、命名空间&#xff08;namespace&…

双周回顾#007 - 前端与后端

前端的问题不是难&#xff0c;而是它面对最终用户。只要用户的喜好和口味发生变化&#xff0c;前端就必须跟上。 这导致前端不得不快速变化&#xff0c;因为用户的口味正在越来越快地改变。 后端不需要面对最终用户&#xff0c;需要解决的都是一些经典的计算机科学问题&#…

第16章-DNS

目录 1. 域名 1.1 产生背景 1.2 概述 1.3 域名的树形层次化结构 2. DNS 2.1 概述 2.2 工作机制 3. DNS查询模式 3.1 递归查询&#xff1a; 3.2 迭代查询&#xff1a; 4. 相关知识点 4.1 集中式DNS 4.2 国内通用DNS 4.3 配置DNS代理 1. 域名 1.1 产生背景 ① IP…

DataIntegrityViolationException异常产生原因及解决方案

DataIntegrityViolationException异常产生原因及解决方案 01 异常的发生场景 在我新写了一个接口之后出现的 //org.springframework.dao.DataIntegrityViolationException日志报错的意思是参数设置了一个错误的值 02 异常的产生及其原因 我最开始认为是MySQL数据库表设计…

JMeter性能测试基本过程及示例

jmeter 为性能测试提供了一下特色&#xff1a; jmeter 可以对测试静态资源&#xff08;例如 js、html 等&#xff09;以及动态资源&#xff08;例如 php、jsp、ajax 等等&#xff09;进行性能测试 jmeter 可以挖掘出系统最大能处理的并发用户数 jmeter 提供了一系列各种形式的…

Acwing 每日一题 空调 差分 贪心

&#x1f468;‍&#x1f3eb; 空调 &#x1f468;‍&#x1f3eb; 参考题解 import java.util.Scanner;public class Main {static int N (int) 1e5 10;static int[] a new int[N];static int n;public static void main(String[] args){Scanner sc new Scanner(System.…

07 编译器

目录 编译过程编译器查看详解函数库自动化构建工具进度条程序 1. 编译过程 预处理: a. 去注释 b.宏替换 c.头文件展开 d.条件编译 编译: 汇编 汇编: 可重定向二进制目标文件 链接: 链接多个.o, .obj合并形成一个可执行exe gcc编译c程序, g编译c程序 2. 编译器查看 输入gcc …

ABAP - SALV教程04 添加状态栏

CL_SALV_TABLE中提供了 GET_FUNCTIONS方法.GET_FUNCTIONS方法返回的是一个CL_SALV_FUNCTIONS_LIST类型的实例对象. 类CL_SALV_FUNCTIONS_LIST两个方法(SET_ALL、SET_DEFAULT)可以添加标准状态栏 实现步骤: 定义SET_STATUS私有方法. PRIVATE SECTION.METHODS:set_status CHA…

Revit-二开之创建墙-(6)

Revit API窗间墙 protected override Result OnExecute(ExternalCommandData commandData, ref string message, ElementSet elements) {try{// 获取当前活动的文档

qsort使用

qsort 是用来排序的数据的库函数,底层使用的是快速排序的方式 排序方式有:选择,冒泡,插入,快速, 希尔...... 对于qsort这个库函数: void qsort(void* base,size_t num,size_t size,int (*compar)(const void*,const void*) 其中 void* base 是指针,指向的是待排序的数组的第…