若依管理系统自学笔记一:Spring Security

本文通过实践项目的方式介绍了SpringSecurity的基本配置及使用方法,包括依赖导入、自定义登录逻辑、配置类详解等内容,并提供了一个完整的示例项目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目前技术栈:Java基础,JavaWeb部分,SSM,SpringBoot,前端(html+js部分+vue部分),基本上都只学了理论而未实践

学习目的:想在学习若依管理系统的同时,回顾SpringBoot等知识,并掌握SpringSecurity以及vue的部分知识。学习完成后,完成自己的第一个项目。

学习资源:
http://doc.ruoyi.vip/ruoyi-vue/document/hjbs.html#准备工作
https://blog.youkuaiyun.com/donglinjob/category_10352718.html
https://blog.youkuaiyun.com/zimou5581/category_8892656.html

强烈推荐:目前看到最全最完整写的最好的
https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzI1NDY0MTkzNQ==&action=getalbum&album_id=1319828555819286528&scene=173&from_msgid=2247489405&from_itemidx=2&count=3&nolastread=1#wechat_redirect

补充:本文以实践为主,所以各个知识点的原理不会深入,可以参考学习资料中的原理部分。在每篇博文的最后,会有demo仿写,仿写代码放在github了:资源地址

Spring Security

学习建议:准备一个空的springboot项目和若依的项目,按照博客中的内容一边实践一边学习若依的代码。

Spring Security 是一个高度自定义的 安全框架。利用 Spring IoC/DI和 AOP 功能,为系统提供了声明式安全访问控制功能,减少了为系统安全而编写大量重复代码的工作。

Spring Security是基于Spring的一个安全框架,最重要的功能是认证和授权,核心是 过滤器链 springSecurityFilterChain 【类型是FilterChainProxy】,过滤器链的每个元素都是一组URL对应一组过滤器,WebSecurity用来创建FilterChainProxy过滤器,HttpSecurity用来创建过滤器链的每个元素,具体的实现请看 4. SpringSecurity配置类

设计模式使用到了 适配器模式建造者模式
博文推荐:
https://blog.youkuaiyun.com/zimou5581/article/details/102457672
https://blog.youkuaiyun.com/u012702547/article/details/107655180
深入源码级别的原理剖析,值得一看
在这里插入图片描述

SpringSecurity知识点概述

1. 依赖导入 + 测试

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

导入后,Spring Security 已经生效,默认拦截全部请求,如果用户没有登录,跳转到内置登录页面。账号是user,密码在idea的控制台中,是通过UUID随机生成的密码。如果密码输入错误,跳转到login?error:
请添加图片描述

注意,在 Spring Security 中,如果我们不做任何配置,默认的登录页面和登录接口的地址都是 /login,也就是说,默认会存在如下两个请求:

GET http://localhost:8080/login
POST http://localhost:8080/login

如果是 GET 请求表示你想访问登录页面,如果是 POST 请求,表示你想提交登录数据。

4.SecurityConfig 中,我们可以通过 loginProcessingUrl 方法来指定登录接口地址,如下:

.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/doLogin")
.permitAll()
.and()

这样配置之后,登录页面地址和登录接口地址就分开了,各是各的。

此时我们还需要修改登录页面里边的 action 属性,改为 /doLogin,如下:

<form action="/doLogin" method="post">
<!--省略-->
</form>

2. UserDetailsService接口 自定义账号密码

当没有配置的时候,账号和密码是由 Spring Security 定义生成的。而在实际项目中账号和密码都是从数据库中查询出来的。所以若我们要通过自定义逻辑控制认证逻辑,必须实现 UserDetailsService 接口以及拥有PasswordEncoder实例。 我们先来讲UserDetailsService。

2.1 接口定义:

请添加图片描述

2.2 返回值 UserDetails请添加图片描述

2.3 UserDetails的实现类User

要想返回 UserDetails 的实例就只能返回接口的实现类。Spring Security 中提供了User这样一个实例。
请添加图片描述
我们重点看一下这个构造方法
请添加图片描述
username:用户名

password:密码

authorities:用户具有的权限。此处不允许为 null

此处的用户名应该是客户端传递过来的用户名。而密码应该是从数据库中查询出来的密码。Spring Security 会根据 User 中的 password和客户端传递过来的 password 进行比较。如果相同则表示认证通过,如果不相同表示认证失败。

authorities 里面的权限对于后面学习授权是很有必要的,包含的所有内容为此用户具有的权限,如有里面没有包含某个权限,而在做某个事情时必须包含某个权限则会出现 403。通常都是通过AuthorityUtils.commaSeparatedStringToAuthorityList(“”) 来 创 建authorities 集合对象的。参数是一个字符串,多个权限使用逗号分隔。

2.4 方法参数

方法参数是用户名,默认为username

2.5 异常

UsernameNotFoundException 用 户 名 没 有 发 现 异 常 。 在loadUserByUsername 中是需要通过自己的逻辑从数据库中取值的。如果 通 过 用 户 名 没 有 查 询 到 对 应 的 数 据 , 应 该 抛 出UsernameNotFoundException,系统就知道用户名没有查询到。

2.6 手撕若依源码

我们找到若依对于UserDetailsService的实现类UserDetailsServiceImpl
请添加图片描述

loadUserByUsername方法中,调用了userService去通过username获取user,然后调用createLoginUser方法,传入user和通过permissionService获取的用户权限,去返回一个userDetails。我们点开这个LoginUser,发现它是实现了UserDetails这个接口,也就是它是作者对UserDetails的一个扩展,本质上还是UserDetails。


感想:
UserDetailsServiceImpl中组合了两个Service,并调用autowired自动装配,使得后面可以通过调用这两个服务去获取所需要的信息,体现了IOC的思想。

3 PasswordEncoder 密码解析器

Spring Security 要求容器中必须有 PasswordEncoder 实例。所以当自定义登录逻辑时要求必须给容器注入 PaswordEncoder 的 bean 对象。作用就是给密码编码。

如果我们不使用数据库导入账号密码,可以在yml或者properties文件中配置:

spring.security.user.name=123
spring.security.user.password=123

或者在配置类中:

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   
        auth.inMemoryAuthentication()
                .withUser("javaboy.org")
                .password("123").roles("admin");
    }

3.1 接口介绍

请添加图片描述
可以看到,官方推荐的实例是BCryptPasswordEncoder,其他的解析器可以在idea中查看PasswordEncoder的实现类

encode():把参数按照特定的解析规则进行解析。
matches():验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码匹配,则返回 true;如果不匹配,则返回 false。第一个参数表示需要被解析的密码。第二个参数表示存储的密码。
upgradeEncoding():如果解析的密码能够再次进行解析且达到更安全的结果则返回 true,否则返回 false。默认返回 false。

3.2 BCryptPasswordEncoder

BCryptPasswordEncoder 是 Spring Security 官方推荐的密码解析器,平时多使用这个解析器。
BCryptPasswordEncoder 是对 bcrypt 强散列方法的具体实现。是基于 Hash 算法实现的单向加密。可以通过 strength 控制加密强度,默认 10.

3.3 手撕若依源码

请添加图片描述
这地方很简单,就直接new一个实例就好了,然后在身份认证接口加密一下,身份认证接口请看4.3。

4. SpringSecurity配置类

4.1 @EnableGlobalMethodSecurity 注解

我们可以在配置类上加上这个注解来开启spring方法级别安全。

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
注解详解

prePostEnabled = true 会解锁 @PreAuthorize@PostAuthorize 两个注解。从名字就可以看出 @PreAuthorize 注解会在方法执行前进行验证,而 @PostAuthorize 注解会在方法执行后进行验证。

打开若依里的一个Controller,我以SysConfigController为例子,我们可以看到这个类里用了多处 @PreAuthorize 注解,注解里面的是作者自定义的一个注解,封装了他自定义的安全检测。

补充:@EnableWebSecurity 是真正核心的安全注解,不过在导入spring-boot-start-security的时候已经自动注入了,所以不用担心。

4.2 WebSecurityConfigurerAdapter 抽象类

请添加图片描述

这个类是我们配置类必须要继承的抽象类,最重要的是以HttpSecurity或者AuthenticationManagerBuilder作为参数的两个configure,前者是对URL配置,后者是身份验证。

4.2.1 configure(HttpSecurity http) URL配置

请添加图片描述
httpSecurity的方法特别的多,我这里放一些比较常用的以及若依注释里面写的
在这里插入图片描述
请添加图片描述

4.2.1.1 登录成功重定向 URL

在 Spring Security 中,和登录成功重定向 URL 相关的方法有两个:
defaultSuccessUrl
successForwardUrl
这两个咋看没什么区别,实际上内藏乾坤。
首先我们在配置的时候,defaultSuccessUrl 和 successForwardUrl 只需要配置一个即可,具体配置哪个,则要看你的需求,两个的区别如下:
1.defaultSuccessUrl 有一个重载的方法,我们先说一个参数的 defaultSuccessUrl 方法。如果我们在 defaultSuccessUrl 中指定登录成功的跳转页面为 /index,此时分两种情况,如果你是直接在浏览器中输入的登录地址,登录成功后,就直接跳转到 /index,如果你是在浏览器中输入了其他地址,例如 http://localhost:8080/hello,结果因为没有登录,又重定向到登录页面,此时登录成功后,就不会来到 /index ,而是来到 /hello 页面。
2.defaultSuccessUrl 还有一个重载的方法,第二个参数如果不设置默认为 false,也就是我们上面的的情况,如果手动设置第二个参数为 true,则 defaultSuccessUrl 的效果和successForwardUrl 一致。
3.successForwardUrl 表示不管你是从哪里来的,登录后一律跳转到 successForwardUrl 指定的地址。例如 successForwardUrl 指定的地址为 /index ,你在浏览器地址栏输入 http://localhost:8080/hello,结果因为没有登录,重定向到登录页面,当你登录成功之后,就会服务端跳转到 /index 页面;或者你直接就在浏览器输入了登录页面地址,登录成功后也是来到 /index。

这让我想到了我们去看某些网站的文章时,比如csdn,有些文章要我们先登录才能看,它会跳转到登录页面,登录成功后,会跳回原页面,这可能就是使用了 defaultSuccessUrl
而有些网站我们登录成功后,会跳转到首页,这可能就是使用了 successForwardUrl
虽然允许两个都配置,但建议只配置一个,有兴趣的可以试试看两个都配置的时候,是哪个配置被覆盖掉了。

4.2.1.2 注销登录接口

注销登录的默认接口是 /logout,我们也可以配置。

.and()
.logout()
.logoutUrl("/logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout","POST"))
.logoutSuccessUrl("/index")
.deleteCookies()
.clearAuthentication(true)
.invalidateHttpSession(true)
.permitAll()
.and()

注销登录的配置:
默认注销的 URL 是 /logout,是一个 GET 请求,我们可以通过 logoutUrl 方法来修改默认的注销 URL。
logoutRequestMatcher 方法不仅可以修改注销 URL,还可以修改请求方式,实际项目中,这个方法和 logoutUrl 任意设置一个即可。
logoutSuccessUrl 表示注销成功后要跳转的页面。
deleteCookies 用来清除 cookie。
clearAuthentication 和 invalidateHttpSession 分别表示清除认证信息和使 HttpSession 失效,默认可以不用配置,默认就会清除。

4.2.1.3 rememberMe

加了这个方法以后,就可以实现自动登录,cookie中多传了一个remember-me=on;
在浏览器关闭后,并重新打开之后,用户再去访问接口,此时会携带着 cookie 中的 remember-me 到服务端,服务到拿到值之后,可以方便的计算出用户名和过期时间,再根据用户名查询到用户密码,然后通过 MD5 散列函数计算出散列值,再将计算出的散列值和浏览器传递来的散列值进行对比,就能确认这个令牌是否有效。

4.2.2 configure(WebSecurity web)

一般是用来绕开静态资源访问的

@Override
public void configure(WebSecurity web) throws Exception {
   
    web.ignoring().antMatchers("/js/**", "/css/**","/images/**");
}

这里要注意区分它和下面代码的区别

@Override
protected void configure(HttpSecurity http) throws Exception {
   
    http.antMatchers(
             
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值