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笔记相关推荐

  1. Java -考研 学习路线(笔记链接汇总)-个人用

    文章目录 1. Java 学习路线 1.1 JavaSE 1.1.1 C 语言基础 1.1.2 面对对象程序设计C++ 1.1.3 Java 基础 1) 基础 2) GUI 3) 网络编程 4) 多线 ...

  2. 阿里巴巴最新开源“SpringSecurity手册”用户+案例+认证+框架,面面俱到太全了

    pringSecurity 相信Spring大家一定不陌生,那么SpringSecurity你又了解多少呢?市面上有关Spring的介绍有很多,那么对于SpringSecurity只有一些简单的有关概 ...

  3. Spring Boot 框架学习笔记(五)( SpringSecurity安全框架 )

    Spring Boot 框架学习笔记(五) SpringSecurity安全框架 概述 作用 开发示例: 1. 新建项目 2. 引入依赖 3. 编写`SecurityConfig`类,实现认证,授权, ...

  4. 狂神SpringSecurity学习笔记(基础)

    文章目录 前言 一.为什么使用SpringSecurity 一.关于SpringSecurity 导入模板素材 定制首页 前言 笔记整理来源于狂神视频https://www.bilibili.com/ ...

  5. Springboot学习笔记(四)SpringSecurity.Shiro

    前言: 学习B站UP主狂神说视频笔记整理视频链接 SpringSecurity 安全简介 Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架.它实际上是保护基于sprin ...

  6. springSecurity的学习笔记--使用spring-Security完成表单登陆,手机验证码登陆,第三方登陆

    环境搭建好后,之后的练习进入了一个十分痛苦的阶段!! 但是与此同时,收获也是比较可观的. 老师通过详细的视频讲解,完成了表单登陆,包括账号密码和验证码登陆,手机验证码登陆,第三方登陆. 每一个部分都进 ...

  7. SpringSecurity动态权限笔记

    前言 要想实现动态配置URL权限,就要自定义权限配置 数据库 那总的来说,大概是怎么一个流程呢? 首先先创建对应数据表Bean 创建Bean public class Role {private In ...

  8. SpringSecurity以及Oauth2(笔记)

    文章目录 一.简介 1.1 安全框架 1.2 常用安全框架 二.SpringSecurity 2.1 快速开始 2.2 Security的部分源码 2.3 自定义登陆逻辑 2.4 自定义登陆页面 2. ...

  9. SpringSecurity 学习笔记分享 记录历程开篇

    基础翻译篇 官方文档地址 GitHub demo 代码 简介 Spring Security 是一个提供身份验证,授权,保护以及防止常见攻击的框架,由于同时支持响应式和命令式,是spring框架的安全 ...

最新文章

  1. python过去日期_利用python获取当前日期前后N天或N月日期的方法示例
  2. 马自达新车全面取消触摸屏,意外引得叫好声一片
  3. c语言习题与实验doc,[教材]C语言程序设计习题与上机实验(全部答案).doc
  4. Spring框架分为哪七大模块以及各模块的主要功能作用
  5. Windows最全快捷键
  6. 「LibreOJ NOIP Round #1」旅游路线
  7. struts2面试整理
  8. Leetcode 437.路径总和III
  9. 自己动手写操作系统(高清图书+源代码)分享
  10. 高频量化交易之王--李庆在华尔街
  11. 深度学习--十折交叉验证
  12. 操作系统是介于计算机硬件和用户之间的接口,计算机操作系统知识盘点
  13. 学习记录——VGG16跑cifar10数据集
  14. self._handle = _dlopen(self._name, mode) OSError: [WinError 126] 找不到指定的模块
  15. 0x00000....蓝屏
  16. java.lang.IllegalStateException: Did you forget to call 'public void setup(LocalActivityManager acti
  17. html樱花飘落代码_武大樱花又盛开,用python画一棵樱花树
  18. 计算机桌面有一条红线去不掉,我的电脑桌面屏幕最下方存在一条大概2mm红线请问正常吗...
  19. MGN:Learning Discriminative Features with Multiple Granularities for Person Re-Identification阅读笔记
  20. ZeroBrane Studio远程调试Cocos2d-x的Lua脚本

热门文章

  1. php 乘法表格,excel表格乘法计算方法
  2. 我们失去了,我们又没有失去什么
  3. CSS权重中的越级现象
  4. toFixed用法总结
  5. ArcGIS栅格图像导出为TIF格式
  6. 精读《关键对话》:沉默与对抗之外的第三种选择
  7. 面试:自定义view / viewgroup 相关问题
  8. bat 复制文件夹内容到指定文件夹
  9. 大话卫星导航中的信号处理系列文章——GPS信号以及C/A码生成
  10. 平江县计算机学校怎么样,平江县职业技术学校评价怎么样