SpringSecurity笔记
1. Spring Security学习
1.1 简介
安全框架就是解决系统安全的框架。如果没有安全框架,我们需要手动的处理每个资源的访问控制,这是非常麻烦的。使用了安全框架,我们可以通过配置的方式实现对资源的访问限制。
1.2 核心功能
- 认证 (你是谁)
认证:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。通俗点说就是系统认为用户是否能登录
- 授权 (你能干什么)
授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情。
- 攻击防护 (防止伪造身份)
2. Spring Security 入门
2.1 权限管理中的相关概念
2.1.1 主体(principal)
principal:使用系统的用户或设备或从其他系统远程登录的用户等等。简单说就是谁使用系统谁就是主体
2.1.2 认证(authentication)
权限管理系统确认一个主体的身份,允许主体进入系统。简单说就是“主体”证明自己是谁。
笼统的认为就是以前所做的登录操作。
2.1.3 授权(authorization)
将操作系统的“权力”“授予”“主体”,这样主体就具备了操作系统中特定功能的能力。
所以简单来说,授权就是给用户分配权限。
2.2 UserDetailsService相关API的介绍
2.2.1 UserDetailsService接口
如果要实现自定义登录逻辑
就需要实现UserDetailsService接口 重写loadUserByUsername方法
用户的登录是访问这个接口的唯一方法loadUserByUsername的,这个方法接收一个参数,就是用户名,如果没有,会抛异常。如果有,返回UserDetails。
2.2.2 UserDetails接口
如果 前端传递过来的username 与数据库中的username一致 那就返回UserDetails (但是UserDetails是一个接口 不能直接new一个UserDetails返回 所以Spring Security自身存在一个User类实现了UserDetails接口) 所以实际上返回的是User类。
如果不一致 那就抛出异常。
这个接口有7个抽象方法,值得注意的前3个方法,
第1个方法是获取权限的,第2个方法获取密码,第3个方法获取用户名。
2.2.3 User类
User类实现了UserDetails接口
最终登录成功 返回的是User类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hol7WSrj-1636258908002)(C:\Users\23652\Desktop\Typora个人文档\SpringSecurity\Image\User.png)]
User类的第一个构造方法
这个构造方法有3个参数:用户名、密码、权限。
然后里面调用了重载的第二个构造方法
User类的第二个构造方法
这个重载的构造方法有7个参数,除了用户名、密码、权限外,
还有账号是否启用、是否过期、是否锁定等。
小小的注意点:
方法参数 username
表示用户名。此值是客户端表单传递过来的数据。默认情况下必须叫username,否则无法接收。
(也就是前端传过来的参数名 默认情况下必须为username)
2.3 PasswordEncoder密码解析器
2.3.1 PasswordEncoder简介
PasswordEncoder是Spring Security提供的一个接口,称它为密码解析器,
这个接口主要是处理密码的。(也就是给密码加密)
2.3.2 PasswordEncoder接口方法
第一个方法: String encode(CharSequence rawPassword);
encode()方法是对明文密码进行加密的,返回一个密文。 (推荐使用SHA-1加密算法)
rawPassword参数是前端传递过来的密码
第二个方法: boolean matches(CharSequence rawPassword, String encodedPassword);
matches()是匹配明文密码和密文,返回布尔值。如果匹配成功 返回true 否则返回false
第一个参数rawPassword是明文密码 (也就是客户端传递过来的密码)
第二个参数encodedPassword是已经加密好的密码 (也就是存储在数据库中的密码)
第三个方法: default boolean upgradeEncoding(String encodedPassword);
upgradeEncoding()方法是对密文进行二次加密,这个方法是默认的。
encodedPassword参数是密文
2.3.3 BCryptPasswordEncoder类
PasswordEncoder接口有很多实现类,其中最主要的是官方推荐的BCryptPasswordEncoder类,平时使用的最多的就是这个密码解析器。BCryptPasswordEncoder是对bcrypt强散列方法的具体实现,是基于hash算法的单向加密。可以通过strength来控制强度,默认是10。
源码如下:
encode方法源码解析:
encode方法是对明文密码进行加密,原理是使用一个随机生成的salt,用明文密码加上这个salt来一起进行加密,返回密文,由于这个salt每次生成的都不一样,所以即使明文密码一样,最后加密出来的密文是不一样的,这样保证了用户密码的安全。
matchs方法源码解析:
matchs方法是用来匹配明文密码和密文的,最终结果用布尔值返回。
2.4 自定义登录逻辑
2.4.1 配置类
当我们自定义登录逻辑时,需要用到UserDetailsService和PasswordEncoder,Spring Security要求自定义登录逻辑时容器内必须要有PasswordEncoder实例,不能new出来,因此需要一个配置类来向容器中注入。
创建config包,包下定义SecurityConfig配置类:
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{// 向容器中注入PasswordEncoder实例@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}}
2.4.2 UserDetailsService的实现类
需要有一个类来实现UserDetailsService接口,如下:
@Service public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate PasswordEncoder passwordEncoder;/** 重写loadUserByUsername方法*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//实际是根据用户名去数据库查,这里就直接用静态数据了if(!username.equals("ycz")) {throw new UsernameNotFoundException("用户名不存在!");}//比较密码,匹配成功会返回UserDetails,实际上也会去数据库查String password = passwordEncoder.encode("ycz123456");User user = new User(username,password,AuthorityUtils.commaSeparatedStringToAuthorityList("ycz,admin"));return user;}}
2.5 自定义登录页面
Spring Security提供了登录页面,就是需要验证的那个页面。但是一般在实际项目中会用自己定义好的登录页面,如果想用自己定义好的登录页面,比如这里的login.html,只需要修改配置类即可。
修改后的配置类如下: (Spring Security配置类 必须得继承WebSecurityConfigurerAdapter类 并重写configure方法)
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin().loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//登录成功后跳转到的页面,只接受post请求.successForwardUrl("/toMain");//授权http.authorizeRequests().antMatchers("/login.html").permitAll()//放行的路径.anyRequest().authenticated();//除了放行路径,其他路径都需要授权//关闭csrf防护http.csrf().disable();}// 向容器中注入PasswordEncoder实例@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();} }
LoginController修改如下:
一定要使用redirect进行重定向 跳转到首页 (main.html)
原因:(对于前后端不分离的项目)
由于浏览器不支持post请求 只支持get请求 而登录页面的form表单,只能发送post请求
所以form表单发送post请求 第一次请求(post请求)到达/login接口 返回HTTP状态码(302)之后 第二次请求(get请求)
重定向跳转到main.html页面
main.html页面如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>首页</title> </head> <body> 登陆成功 <a href="main1.html">跳转</a> </body> </html>
@Controller public class LoginController {@RequestMapping("/login")public String login(){return "redirect:main.html";}/** 登录*/@RequestMapping("/toMain")public String main() {return "redirect:main.html";} }
2.6 自定义错误页面
和自定义登录页面一样,修改配置类:
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin().loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//登录成功后跳转到的页面,只接受post请求.successForwardUrl("/toMain")//登录失败后跳转到的页面,只接受post请求.failureForwardUrl("/toError");//授权http.authorizeRequests().antMatchers("/login.html").permitAll()//放行的路径.antMatchers("/error.html").permitAll().anyRequest().authenticated();//除了放行路径,其他路径都需要授权//关闭csrf防护http.csrf().disable();}// 向容器中注入PasswordEncoder实例@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}}
然后LoginController控制器添加:
/** 错误跳转*/@RequestMapping("/toError")public String error() {return "redirect:error.html";}
自定义的错误页面error.html:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>错误页面</title> </head> <body><h2 style="color:red" align="center">登录失败,请重新登录!</h2><a href="/login.html">点击跳转</a> </body> </html>
2.7 自定义用户名和密码参数名
前面写的登录页面的表单:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>login</title> </head> <body><!--默认情况下 用户名必须是username 密码是password--> <form action="/login" method="post"> 用户名:<input type="text" name="username"/><br/> 密码:<input type="password" name="password"/><br/><input type="submit" value="登录"> </form> </body> </html>
默认情况下 用户名参数和密码参数 必须是username和password
提交方法必须是post
提交的地址必须是/login(要与Spring Security的配置文件的相关配置信息,保持一致)
通过这两个方法可以修改参数名称。
修改配置类,如下:
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin()//自定义用户名密码参数名称.usernameParameter("username123").passwordParameter("password123").loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//登录成功后跳转到的页面,只接受post请求.successForwardUrl("/toMain")//登录失败后跳转到的页面,只接受post请求.failureForwardUrl("/toError");//授权http.authorizeRequests().antMatchers("/login.html").permitAll()//放行的路径.antMatchers("/error.html").permitAll().anyRequest().authenticated();//除了放行路径,其他路径都需要授权//关闭csrf防护http.csrf().disable();}// 向容器中注入PasswordEncoder实例@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}}
然后form表单的参数名只需和配置类里设置的一样就可以了:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>login</title> </head> <body> <!--默认情况下 用户名必须是username 密码是password--> <form action="/login" method="post"> 用户名:<input type="text" name="username123"/><br/> 密码:<input type="password" name="password123"/><br/><input type="submit" value="登录"> </form> </body> </html>
2.8 自定义成功登录处理器
2.8.1 默认的successForwardUrl存在的问题
默认的.successForwardUrl("/toMain")跳转,存在的问题:
如下图:(源码)
如上图:(successForwardUrl源码) successForwardUrl底层就是一个forward跳转 但forward跳转是无法跳到应用之外的页面的,不能满足我们实际开发的需求
2.8.2 自定义成功登录处理器实现
创建一个MyAuthenticationSuccessHandler类,实现AuthenticationSuccessHandler接口
要求:
1.定义一个私有的成员变量url
2.创建一个带url参数的构造器
3.重写onAuthenticationSuccess方法(把forward改成Redirect)
参考代码如下:
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {private String url;public MyAuthenticationSuccessHandler(String url) {this.url = url;}@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {}//重写成功登录的跳转 把forward改成Redirect@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {System.out.println(request.getRemoteAddr());// Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();//需要强制类型转换成UserUser user = (User) authentication.getPrincipal();System.out.println(user.getUsername());//密码基于安全考虑 输出nullSystem.out.println(user.getPassword());System.out.println(user.getAuthorities());response.sendRedirect(url);} }
然后配置类里修改如下:
把.successForwardUrl("/toMain")修改为.successHandler(new MyAuthenticationSuccessHandler(“http://www.baidu.com”))
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin()//自定义用户名密码参数名称.usernameParameter("username123").passwordParameter("password123").loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//自定义登录成功处理器.successHandler(new MyAuthenticationSuccessHandler("http://www.baidu.com"))//登录失败后跳转到的页面,只接受post请求.failureForwardUrl("/toError");//授权http.authorizeRequests().antMatchers("/login.html").permitAll()//放行的路径.antMatchers("/error.html").permitAll().anyRequest().authenticated();//除了放行路径,其他路径都需要授权//关闭csrf防护http.csrf().disable();}// 向容器中注入PasswordEncoder实例@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}}
2.9 自定义失败登录处理器
2.9.1 默认的failureForwardUrl存在的问题
存在的问题,和默认的successForwardUrl存在的问题 类似
在此就不过多阐述
2.9.2 自定义失败登录处理器实现
创建一个MyAuthenticationFailureHandler类,实现AuthenticationFailureHandler接口
要求:
1.定义一个私有的成员变量url
2.创建一个带url参数的构造器
3.重写onAuthenticationFailure方法(把forward改成Redirect)
参考代码如下:
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {private String url;public MyAuthenticationFailureHandler(String url) {this.url = url;}//重写登陆失败的跳转 把forward改成Redirect@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {response.sendRedirect(url);} }
然后配置类里修改如下:
把.failureForwardUrl("/toError")修改为.failureHandler(new MyAuthenticationFailureHandler("/error.html"))
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin()//自定义用户名密码参数名称.usernameParameter("username123").passwordParameter("password123").loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//自定义登录成功处理器.successHandler(new MyAuthenticationSuccessHandler("http://www.baidu.com"))//自定义登陆失败处理器.failureHandler(new MyAuthenticationFailureHandler("/error.html"));//授权http.authorizeRequests().antMatchers("/login.html").permitAll()//放行的路径.antMatchers("/error.html").permitAll().anyRequest().authenticated();//除了放行路径,其他路径都需要授权//关闭csrf防护http.csrf().disable();}// 向容器中注入PasswordEncoder实例@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}}
注意点:
事实上在实际开发中基本都是前后端分离的,使用的都是这两种处理器的方式来进行跳转。
2.10 关于授权配置
2.10.1 anyRequest
anyRequest代表的是所有请求,Spring Security的要求是这个(anyRequest)必须要放在最后面,如下:
可以理解为拦截器,放行的路径放在前面,从前往后执行,除了放行路径之外的其他路径都需要进行认证才能访问。
2.10.2 antMatchers
这个是放行的路径,放行的路径不需要进行Spring Security即可访问,
比如项目的一些css、js、图片等静态资源全部需要放行,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJWclMuL-1636258908012)(C:\Users\23652\Desktop\Typora个人文档\SpringSecurity\Image\antMatchers.png)]
注意点: 1.也可以使用ant匹配表达式,来控制放行路径 例:.antMatchers("/**/*.png").permitAll() 2.使用正则表达式来匹配放行路径 例:.regexMatchers(".+[.]png").permitAll()
2.11 角色权限判断
2.11.1 基于权限判断
判断用户是否具有特定的权限,用户的权限是在自定义登录逻辑中创建User对象时指定的。
如下:
这里直接指定了两个静态的数据(admin,normal)作为权限
主页面(main.html)中加一个跳转:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> 登陆成功 <a href="main1.html">跳转</a> </body> </html>
main1.html页面如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 权限控制!!! </body> </html>
修改Spring Security配置文件:
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin().loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//登录成功后跳转到的页面,只接受post请求.successForwardUrl("/toMain")//登录失败后跳转到的页面,只接受post请求.failureForwardUrl("/toError");//授权http.authorizeRequests().antMatchers("/login.html").permitAll()//放行的路径.antMatchers("/error.html").permitAll()//权限控制 拥有admin权限才能访问main1.html.antMatchers("/main1.html").hasAuthority("admin").anyRequest().authenticated();//除了放行路径,其他路径都需要授权//关闭csrf防护http.csrf().disable();}// 向容器中注入PasswordEncoder实例@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}}
**注意:**表示用户拥有admin这个权限,才能访问main1.html这个页面
还有一种情况,如果这个用户有多个权限的情况。
如下所示:(修改Spring Security配置文件)
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin().loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//登录成功后跳转到的页面,只接受post请求.successForwardUrl("/toMain")//登录失败后跳转到的页面,只接受post请求.failureForwardUrl("/toError");//授权http.authorizeRequests().antMatchers("/login.html").permitAll()//放行的路径.antMatchers("/error.html").permitAll()//权限控制 拥有admin权限才能访问main1.html.antMatchers("/main1.html").hasAuthority("admin","ADMIN").anyRequest().authenticated();//除了放行路径,其他路径都需要授权//关闭csrf防护http.csrf().disable();}// 向容器中注入PasswordEncoder实例@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}}
注意:表示用户只需要拥有"admin","ADMIN"两个权限的其中之一,就可以访问main1.html页面
2.11.2 基于角色判断
解释:
如果用户具备给定角色就允许访问。否则出现 403。 参数取值来源于自定义登录逻辑 UserDetailsService实现类中创建 User 对象时给User赋予的授权。
判断用户是否有特定的角色,和用户的权限一样,
用户的角色也是在自定义登录逻辑中创建User对象时指定的。
修改UserDetailsService的实现类:
注意:角色定义必须以ROLE_开头,后面跟上角色名称,这是固定格式。
例:ROLE_abc 的角色名称是abc
修改Spring Security配置文件:
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin().loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//登录成功后跳转到的页面,只接受post请求.successForwardUrl("/toMain")//登录失败后跳转到的页面,只接受post请求.failureForwardUrl("/toError");//授权http.authorizeRequests().antMatchers("/login.html").permitAll()//放行的路径.antMatchers("/error.html").permitAll()//权限控制 拥有admin权限才能访问main1.html//.antMatchers("/main1.html").hasAuthority("admin","ADMIN")//访问main1.html 必须得拥有abc角色 角色名称不能加ROLE_前缀.antMatchers("/main1.html").hasRole("abc").anyRequest().authenticated();//除了放行路径,其他路径都需要授权//关闭csrf防护http.csrf().disable();}// 向容器中注入PasswordEncoder实例@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}}
还有一种情况,如果这个用户有多个角色。
处理方式和上述的基于权限的处理方式类似。
.antMatchers("/main1.html").hasRole("abc")//修改为 .antMatchers("/main1.html").hasAnyRole("abc","ABC")//类似于这样子的情况,用户拥有权限abc,ABC
2.11.3 基于IP地址控制
只允许指定的IP地址访问,其他的IP拒绝访问,如下:
修改Spring Security配置文件:
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{@Override protected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin().loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//登录成功后跳转到的页面,只接受post请求.successForwardUrl("/toMain")//登录失败后跳转到的页面,只接受post请求.failureForwardUrl("/toError");//授权http.authorizeRequests().antMatchers("/login.html").permitAll()//放行的路径.antMatchers("/error.html").permitAll()//权限控制 拥有admin权限才能访问main1.html.antMatchers("/main1.html").hasAuthority("admin","ADMIN")//基于ip地址.antMatchers("/main1.html").hasIpAddress("127.0.0.1").anyRequest().authenticated();//除了放行路径,其他路径都需要授权//关闭csrf防护http.csrf().disable(); }// 向容器中注入PasswordEncoder实例 @Bean public PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder(); }}
也就是添加,.antMatchers("/main1.html").hasIpAddress(“127.0.0.1”)这一段代码
2.12 自定义403处理方案
http状态码403出现的原因:用户权限不足。
实现步骤:
第一步:新建MyAccessDeniedHandler类,实现AccessDeniedHandler 接口
第二步:重写AccessDeniedHandler 接口的handle方法
第三步:在Spring Security配置类中,添加相对应的配置。
自定义的MyAccessDeniedHandler类,代码如下:
/** * 自定义403处理方案 */ @Component public class MyAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {//响应状态response.setStatus(HttpServletResponse.SC_FORBIDDEN);//设置状态码 403//设置返回数据格式 json字符串response.setHeader("Content-Type","application/json;charset=utf-8");PrintWriter printWriter=response.getWriter();printWriter.write("{\"status\":\"error\",\"msg\":\"权限不足,请联系管理员\"}");printWriter.flush();printWriter.close();} }
修改Spring Security配置文件:
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{//注入异常处理的实现类@Autowiredprivate MyAccessDeniedHandler myAccessDeniedHandler;@Override protected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin().loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//登录成功后跳转到的页面,只接受post请求.successForwardUrl("/toMain")//登录失败后跳转到的页面,只接受post请求.failureForwardUrl("/toError");//异常处理http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);//授权http.authorizeRequests().antMatchers("/login.html").permitAll()//放行的路径.antMatchers("/error.html").permitAll()//权限控制 拥有admin权限才能访问main1.html.antMatchers("/main1.html").hasAuthority("admin","ADMIN")//基于ip地址.antMatchers("/main1.html").hasIpAddress("127.0.0.1").anyRequest().authenticated();//除了放行路径,其他路径都需要授权//关闭csrf防护http.csrf().disable(); }// 向容器中注入PasswordEncoder实例 @Bean public PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder(); }}
添加 :http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);这一段代码(异常处理)
2.13 基于表达式的访问控制
2.13.1 内置的access()方法
先看一下之前的基于角色、权限、IP地址以及内置的访问控制的底层:
access表达式,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WYbIUtsc-1636258908016)(C:\Users\23652\Desktop\Typora个人文档\SpringSecurity\Image\access表达式.png)]
测试access表达式
修改Spring Security配置文件:
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{//注入异常处理的实现类@Autowiredprivate MyAccessDeniedHandler myAccessDeniedHandler;@Override protected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin().loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//登录成功后跳转到的页面,只接受post请求.successForwardUrl("/toMain")//登录失败后跳转到的页面,只接受post请求.failureForwardUrl("/toError");//异常处理http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);//授权http.authorizeRequests().antMatchers("/login.html").access("permitAll")//放行的路径.antMatchers("/error.html").access("permitAll")//放行的路径//权限控制 拥有admin权限才能访问main1.html.antMatchers("/main1.html").hasAnyRole("admin","ADMIN")//基于ip地址.antMatchers("/main1.html").hasIpAddress("127.0.0.1").anyRequest().authenticated();//除了放行路径,其他路径都需要授权//关闭csrf防护http.csrf().disable(); }// 向容器中注入PasswordEncoder实例 @Bean public PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder(); }}
2.13.2 自定义方法
在实际项目中很有可能出现需要自己自定义逻辑的情况。比如判断登录用户是否具有访问当前URL权限。
实现步骤:
第一步:新建接口(MyService),创建hasPermission方法
public interface MyService {public boolean hasPermission(HttpServletRequest request, Authentication authentication); }
第二步:新建实现类,继承MyService接口,重写hasPermission方法
/** * 自定义access方法 * 根据URI 自定义访问权限控制 (URI可以理解为 /main.html) */ @Service public class MyServiceImpl implements MyService {/**** @param request* @param authentication 身份验证* @return*/@Overridepublic boolean hasPermission(HttpServletRequest request, Authentication authentication) {//获取主体Object obj = authentication.getPrincipal();//判断主体是否属于UserDetailsif(obj instanceof UserDetails){//强制类型转换UserDetails userDetails=(UserDetails)obj;//获取权限Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();//判断请求的URI是否在权限里面return authorities.contains(new SimpleGrantedAuthority(request.getRequestURI()));}return false;} }
第三步:
修改Spring Security配置文件:
/* * Spring Security配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{//注入异常处理的实现类@Autowiredprivate MyAccessDeniedHandler myAccessDeniedHandler;@Override protected void configure(HttpSecurity http) throws Exception {//表单提交http.formLogin().loginPage("/login.html")//自定义登录页面//必须和表单提交的接口路径一致,会去执行自定义登录逻辑.loginProcessingUrl("/login")//登录成功后跳转到的页面,只接受post请求.successForwardUrl("/toMain")//登录失败后跳转到的页面,只接受post请求.failureForwardUrl("/toError");//异常处理http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);//授权http.authorizeRequests().antMatchers("/login.html").access("permitAll")//放行的路径.antMatchers("/error.html").access("permitAll")//放行的路径//权限控制 拥有admin权限才能访问main1.html.antMatchers("/main1.html").hasAnyRole("admin","ADMIN")//基于ip地址.antMatchers("/main1.html").hasIpAddress("127.0.0.1").anyRequest().authenticated();//除了放行路径,其他路径都需要授权//自定义access方法.anyRequest().access("@myServiceImpl.hasPermission(request,authentication)");//关闭csrf防护http.csrf().disable(); }// 向容器中注入PasswordEncoder实例 @Bean public PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder(); }}
添加.anyRequest().access("@myServiceImpl.hasPermission(request,authentication)");这一段配置
第四步:
在自定义登录逻辑的实现类(UserDetailServiceImpl)添加权限,(也就是添加/main.html权限)
//自定义登录逻辑 @Service public class UserDetailServiceImpl implements UserDetailsService {//注入passwordEncoder@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {System.out.println("执行自定义登录逻辑");//1.根据用户名去数据库查询 如果不存在抛UsernameNotFoundException异常if(!"admin".equals(username)){throw new UsernameNotFoundException("用户名不存在");}//2.比较密码 (注册时已经加密过) 如果匹配成功 返回UserDetails//AuthorityUtils.commaSeparatedStringToAuthorityList为权限String password = passwordEncoder.encode("123");// /main1.html 是request.getRequestURI()对应的结果return new User(username,password,AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_abc,/main.html"));//设置角色必须以ROLE_开头 角色名称为abc} }
Encoder实例
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}}
添加.anyRequest().access("@myServiceImpl.hasPermission(request,authentication)");这一段配置第四步:在自定义登录逻辑的实现类(UserDetailServiceImpl)添加权限,(也就是添加/main.html权限)```java //自定义登录逻辑 @Service public class UserDetailServiceImpl implements UserDetailsService {//注入passwordEncoder@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {System.out.println("执行自定义登录逻辑");//1.根据用户名去数据库查询 如果不存在抛UsernameNotFoundException异常if(!"admin".equals(username)){throw new UsernameNotFoundException("用户名不存在");}//2.比较密码 (注册时已经加密过) 如果匹配成功 返回UserDetails//AuthorityUtils.commaSeparatedStringToAuthorityList为权限String password = passwordEncoder.encode("123");// /main1.html 是request.getRequestURI()对应的结果return new User(username,password,AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_abc,/main.html"));//设置角色必须以ROLE_开头 角色名称为abc} }
后续内容会继续更新
敬请期待。。。。。。
本人学生党一枚,第一次写博客。如有问题,麻烦各位大哥,高抬贵手,见谅!
SpringSecurity笔记相关推荐
- Java -考研 学习路线(笔记链接汇总)-个人用
文章目录 1. Java 学习路线 1.1 JavaSE 1.1.1 C 语言基础 1.1.2 面对对象程序设计C++ 1.1.3 Java 基础 1) 基础 2) GUI 3) 网络编程 4) 多线 ...
- 阿里巴巴最新开源“SpringSecurity手册”用户+案例+认证+框架,面面俱到太全了
pringSecurity 相信Spring大家一定不陌生,那么SpringSecurity你又了解多少呢?市面上有关Spring的介绍有很多,那么对于SpringSecurity只有一些简单的有关概 ...
- Spring Boot 框架学习笔记(五)( SpringSecurity安全框架 )
Spring Boot 框架学习笔记(五) SpringSecurity安全框架 概述 作用 开发示例: 1. 新建项目 2. 引入依赖 3. 编写`SecurityConfig`类,实现认证,授权, ...
- 狂神SpringSecurity学习笔记(基础)
文章目录 前言 一.为什么使用SpringSecurity 一.关于SpringSecurity 导入模板素材 定制首页 前言 笔记整理来源于狂神视频https://www.bilibili.com/ ...
- Springboot学习笔记(四)SpringSecurity.Shiro
前言: 学习B站UP主狂神说视频笔记整理视频链接 SpringSecurity 安全简介 Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架.它实际上是保护基于sprin ...
- springSecurity的学习笔记--使用spring-Security完成表单登陆,手机验证码登陆,第三方登陆
环境搭建好后,之后的练习进入了一个十分痛苦的阶段!! 但是与此同时,收获也是比较可观的. 老师通过详细的视频讲解,完成了表单登陆,包括账号密码和验证码登陆,手机验证码登陆,第三方登陆. 每一个部分都进 ...
- SpringSecurity动态权限笔记
前言 要想实现动态配置URL权限,就要自定义权限配置 数据库 那总的来说,大概是怎么一个流程呢? 首先先创建对应数据表Bean 创建Bean public class Role {private In ...
- SpringSecurity以及Oauth2(笔记)
文章目录 一.简介 1.1 安全框架 1.2 常用安全框架 二.SpringSecurity 2.1 快速开始 2.2 Security的部分源码 2.3 自定义登陆逻辑 2.4 自定义登陆页面 2. ...
- SpringSecurity 学习笔记分享 记录历程开篇
基础翻译篇 官方文档地址 GitHub demo 代码 简介 Spring Security 是一个提供身份验证,授权,保护以及防止常见攻击的框架,由于同时支持响应式和命令式,是spring框架的安全 ...
最新文章
- python过去日期_利用python获取当前日期前后N天或N月日期的方法示例
- 马自达新车全面取消触摸屏,意外引得叫好声一片
- c语言习题与实验doc,[教材]C语言程序设计习题与上机实验(全部答案).doc
- Spring框架分为哪七大模块以及各模块的主要功能作用
- Windows最全快捷键
- 「LibreOJ NOIP Round #1」旅游路线
- struts2面试整理
- Leetcode 437.路径总和III
- 自己动手写操作系统(高清图书+源代码)分享
- 高频量化交易之王--李庆在华尔街
- 深度学习--十折交叉验证
- 操作系统是介于计算机硬件和用户之间的接口,计算机操作系统知识盘点
- 学习记录——VGG16跑cifar10数据集
- self._handle = _dlopen(self._name, mode) OSError: [WinError 126] 找不到指定的模块
- 0x00000....蓝屏
- java.lang.IllegalStateException: Did you forget to call 'public void setup(LocalActivityManager acti
- html樱花飘落代码_武大樱花又盛开,用python画一棵樱花树
- 计算机桌面有一条红线去不掉,我的电脑桌面屏幕最下方存在一条大概2mm红线请问正常吗...
- MGN:Learning Discriminative Features with Multiple Granularities for Person Re-Identification阅读笔记
- ZeroBrane Studio远程调试Cocos2d-x的Lua脚本