安全框架就是解决系统安全的框架。如果没有安全框架,我们需要手动的处理每个资源的访问控制,这是非常麻烦的。使用了安全框架,我们可以通过配置的方式实现对资源的访问限制。1.2 核心功能
- 认证 (你是谁)
认证:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。通俗点说就是系统认为用户是否能登录
- 授权 (你能干什么)
授权指的是验证某个用户是否有权限执行某个 *** 作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情。
- 攻击防护 (防止伪造身份)
2.1.2 认证(authentication)principal:使用系统的用户或设备或从其他系统远程登录的用户等等。简单说就是谁使用系统谁就是主体
2.1.3 授权(authorization)权限管理系统确认一个主体的身份,允许主体进入系统。简单说就是“主体”证明自己是谁。
笼统的认为就是以前所做的登录 *** 作。
2.2 UserDetailsService相关API的介绍 2.2.1 UserDetailsService接口将 *** 作系统的“权力”“授予”“主体”,这样主体就具备了 *** 作系统中特定功能的能力。
所以简单来说,授权就是给用户分配权限。
如果要实现自定义登录逻辑
就需要实现UserDetailsService接口 重写loadUserByUsername方法
2.2.2 UserDetails接口用户的登录是访问这个接口的唯一方法loadUserByUsername的,这个方法接收一个参数,就是用户名,如果没有,会抛异常。如果有,返回UserDetails。
如果 前端传递过来的username 与数据库中的username一致 那就返回UserDetails (但是UserDetails是一个接口 不能直接new一个UserDetails返回 所以Spring Security自身存在一个User类实现了UserDetails接口) 所以实际上返回的是User类。
如果不一致 那就抛出异常。
2.2.3 User类这个接口有7个抽象方法,值得注意的前3个方法,
第1个方法是获取权限的,第2个方法获取密码,第3个方法获取用户名。
User类实现了UserDetails接口
最终登录成功 返回的是User类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hol7WSrj-1636258908002)(C:Users23652DesktopTypora个人文档SpringSecurityImageUser.png)]
User类的第一个构造方法
这个构造方法有3个参数:用户名、密码、权限。
然后里面调用了重载的第二个构造方法
User类的第二个构造方法
这个重载的构造方法有7个参数,除了用户名、密码、权限外,
还有账号是否启用、是否过期、是否锁定等。
小小的注意点:
2.3 PasswordEncoder密码解析器 2.3.1 PasswordEncoder简介方法参数 username
表示用户名。此值是客户端表单传递过来的数据。默认情况下必须叫username,否则无法接收。
(也就是前端传过来的参数名 默认情况下必须为username)
2.3.2 PasswordEncoder接口方法PasswordEncoder是Spring Security提供的一个接口,称它为密码解析器,
这个接口主要是处理密码的。(也就是给密码加密)
第一个方法: String encode(CharSequence rawPassword);
encode()方法是对明文密码进行加密的,返回一个密文。 (推荐使用SHA-1加密算法)
rawPassword参数是前端传递过来的密码
第二个方法: boolean matches(CharSequence rawPassword, String encodedPassword);
matches()是匹配明文密码和密文,返回布尔值。如果匹配成功 返回true 否则返回false
第一个参数rawPassword是明文密码 (也就是客户端传递过来的密码)
第二个参数encodedPassword是已经加密好的密码 (也就是存储在数据库中的密码)
第三个方法: default boolean upgradeEncoding(String encodedPassword);
2.3.3 BCryptPasswordEncoder类upgradeEncoding()方法是对密文进行二次加密,这个方法是默认的。
encodedPassword参数是密文
PasswordEncoder接口有很多实现类,其中最主要的是官方推荐的BCryptPasswordEncoder类,平时使用的最多的就是这个密码解析器。BCryptPasswordEncoder是对bcrypt强散列方法的具体实现,是基于hash算法的单向加密。可以通过strength来控制强度,默认是10。
源码如下:
encode方法源码解析:
encode方法是对明文密码进行加密,原理是使用一个随机生成的salt,用明文密码加上这个salt来一起进行加密,返回密文,由于这个salt每次生成的都不一样,所以即使明文密码一样,最后加密出来的密文是不一样的,这样保证了用户密码的安全。
2.4 自定义登录逻辑 2.4.1 配置类matchs方法源码解析:
matchs方法是用来匹配明文密码和密文的,最终结果用布尔值返回。
2.4.2 UserDetailsService的实现类当我们自定义登录逻辑时,需要用到UserDetailsService和PasswordEncoder,Spring Security要求自定义登录逻辑时容器内必须要有PasswordEncoder实例,不能new出来,因此需要一个配置类来向容器中注入。
创建config包,包下定义SecurityConfig配置类:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{ // 向容器中注入PasswordEncoder实例 @Bean public PasswordEncoder getPasswordEncoder() { return new BCryptPasswordEncoder(); } }
2.5 自定义登录页面需要有一个类来实现UserDetailsService接口,如下:
@Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Override public 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.6 自定义错误页面Spring Security提供了登录页面,就是需要验证的那个页面。但是一般在实际项目中会用自己定义好的登录页面,如果想用自己定义好的登录页面,比如这里的login.html,只需要修改配置类即可。
修改后的配置类如下: (Spring Security配置类 必须得继承WebSecurityConfigurerAdapter类 并重写configure方法)
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Override protected 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实例 @Bean public PasswordEncoder getPasswordEncoder() { return new BCryptPasswordEncoder(); } }LoginController修改如下:
一定要使用redirect进行重定向 跳转到首页 (main.html)
原因:(对于前后端不分离的项目)
由于浏览器不支持post请求 只支持get请求 而登录页面的form表单,只能发送post请求
所以form表单发送post请求 第一次请求(post请求)到达/login接口 返回HTTP状态码(302)之后 第二次请求(get请求)
重定向跳转到main.html页面
main.html页面如下:
首页 登陆成功 跳转@Controller public class LoginController { @RequestMapping("/login") public String login(){ return "redirect:main.html"; } @RequestMapping("/toMain") public String main() { return "redirect:main.html"; } }
2.7 自定义用户名和密码参数名和自定义登录页面一样,修改配置类:
@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() .anyRequest().authenticated();//除了放行路径,其他路径都需要授权 //关闭csrf防护 http.csrf().disable(); } // 向容器中注入PasswordEncoder实例 @Bean public PasswordEncoder getPasswordEncoder() { return new BCryptPasswordEncoder(); } }然后LoginController控制器添加:
@RequestMapping("/toError") public String error() { return "redirect:error.html"; }自定义的错误页面error.html:
错误页面 登录失败,请重新登录! 点击跳转
2.8 自定义成功登录处理器 2.8.1 默认的successForwardUrl存在的问题前面写的登录页面的表单:
login 默认情况下 用户名参数和密码参数 必须是username和password
提交方法必须是post
提交的地址必须是/login(要与Spring Security的配置文件的相关配置信息,保持一致)
通过这两个方法可以修改参数名称。
修改配置类,如下:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Override protected 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实例 @Bean public PasswordEncoder getPasswordEncoder() { return new BCryptPasswordEncoder(); } }然后form表单的参数名只需和配置类里设置的一样就可以了:
login
2.8.2 自定义成功登录处理器实现默认的.successForwardUrl("/toMain")跳转,存在的问题:
如下图:(源码)
如上图:(successForwardUrl源码) successForwardUrl底层就是一个forward跳转 但forward跳转是无法跳到应用之外的页面的,不能满足我们实际开发的需求
2.9 自定义失败登录处理器 2.9.1 默认的failureForwardUrl存在的问题创建一个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; } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException { } //重写成功登录的跳转 把forward改成Redirect @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { System.out.println(request.getRemoteAddr()); // Collection extends GrantedAuthority> authorities = authentication.getAuthorities(); //需要强制类型转换成User User user = (User) authentication.getPrincipal(); System.out.println(user.getUsername()); //密码基于安全考虑 输出null System.out.println(user.getPassword()); System.out.println(user.getAuthorities()); response.sendRedirect(url); } }然后配置类里修改如下:
把.successForwardUrl("/toMain")修改为.successHandler(new MyAuthenticationSuccessHandler(“http://www.baidu.com”))
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Override protected 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实例 @Bean public PasswordEncoder getPasswordEncoder() { return new BCryptPasswordEncoder(); } }
2.9.2 自定义失败登录处理器实现存在的问题,和默认的successForwardUrl存在的问题 类似
在此就不过多阐述
2.10 关于授权配置 2.10.1 anyRequest创建一个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 @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { response.sendRedirect(url); } }然后配置类里修改如下:
把.failureForwardUrl("/toError")修改为.failureHandler(new MyAuthenticationFailureHandler("/error.html"))
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Override protected 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实例 @Bean public PasswordEncoder getPasswordEncoder() { return new BCryptPasswordEncoder(); } }注意点:
事实上在实际开发中基本都是前后端分离的,使用的都是这两种处理器的方式来进行跳转。
2.10.2 antMatchersanyRequest代表的是所有请求,Spring Security的要求是这个(anyRequest)必须要放在最后面,如下:
2.11 角色权限判断 2.11.1 基于权限判断这个是放行的路径,放行的路径不需要进行Spring Security即可访问,
比如项目的一些css、js、图片等静态资源全部需要放行,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJWclMuL-1636258908012)(C:Users23652DesktopTypora个人文档SpringSecurityImageantMatchers.png)]
注意点: 1.也可以使用ant匹配表达式,来控制放行路径 例:.antMatchers("*.png").permitAll() 2.使用正则表达式来匹配放行路径 例:.regexMatchers(".+[.]png").permitAll()
2.11.2 基于角色判断判断用户是否具有特定的权限,用户的权限是在自定义登录逻辑中创建User对象时指定的。
如下:
这里直接指定了两个静态的数据(admin,normal)作为权限
主页面(main.html)中加一个跳转:
首页 登陆成功 跳转main1.html页面如下:
Title 权限控制!!!修改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") .anyRequest().authenticated();//除了放行路径,其他路径都需要授权 //关闭csrf防护 http.csrf().disable(); } // 向容器中注入PasswordEncoder实例 @Bean public PasswordEncoder getPasswordEncoder() { return new BCryptPasswordEncoder(); } }**注意:**表示用户拥有admin这个权限,才能访问main1.html这个页面
还有一种情况,如果这个用户有多个权限的情况。
如下所示:(修改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") .anyRequest().authenticated();//除了放行路径,其他路径都需要授权 //关闭csrf防护 http.csrf().disable(); } // 向容器中注入PasswordEncoder实例 @Bean public PasswordEncoder getPasswordEncoder() { return new BCryptPasswordEncoder(); } }注意:表示用户只需要拥有"admin","ADMIN"两个权限的其中之一,就可以访问main1.html页面
2.11.3 基于IP地址控制解释:
如果用户具备给定角色就允许访问。否则出现 403。 参数取值来源于自定义登录逻辑 UserDetailsService实现类中创建 User 对象时给User赋予的授权。判断用户是否有特定的角色,和用户的权限一样,
用户的角色也是在自定义登录逻辑中创建User对象时指定的。
修改UserDetailsService的实现类:
注意:角色定义必须以ROLE_开头,后面跟上角色名称,这是固定格式。
例:ROLE_abc 的角色名称是abc
修改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") //访问main1.html 必须得拥有abc角色 角色名称不能加ROLE_前缀 .antMatchers("/main1.html").hasRole("abc") .anyRequest().authenticated();//除了放行路径,其他路径都需要授权 //关闭csrf防护 http.csrf().disable(); } // 向容器中注入PasswordEncoder实例 @Bean public PasswordEncoder getPasswordEncoder() { return new BCryptPasswordEncoder(); } }还有一种情况,如果这个用户有多个角色。
处理方式和上述的基于权限的处理方式类似。
.antMatchers("/main1.html").hasRole("abc")//修改为 .antMatchers("/main1.html").hasAnyRole("abc","ABC")//类似于这样子的情况,用户拥有权限abc,ABC
2.12 自定义403处理方案只允许指定的IP地址访问,其他的IP拒绝访问,如下:
修改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.13 基于表达式的访问控制 2.13.1 内置的access()方法http状态码403出现的原因:用户权限不足。
实现步骤:
第一步:新建MyAccessDeniedHandler类,实现AccessDeniedHandler 接口
第二步:重写AccessDeniedHandler 接口的handle方法
第三步:在Spring Security配置类中,添加相对应的配置。
自定义的MyAccessDeniedHandler类,代码如下:
@Component public class MyAccessDeniedHandler implements AccessDeniedHandler { @Override public 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配置文件:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{ //注入异常处理的实现类 @Autowired private 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 自定义方法先看一下之前的基于角色、权限、IP地址以及内置的访问控制的底层:
access表达式,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WYbIUtsc-1636258908016)(C:Users23652DesktopTypora个人文档SpringSecurityImageaccess表达式.png)]
测试access表达式
修改Spring Security配置文件:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{ //注入异常处理的实现类 @Autowired private 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(); } }
在实际项目中很有可能出现需要自己自定义逻辑的情况。比如判断登录用户是否具有访问当前URL权限。
实现步骤:
第一步:新建接口(MyService),创建hasPermission方法
public interface MyService { public boolean hasPermission(HttpServletRequest request, Authentication authentication); }第二步:新建实现类,继承MyService接口,重写hasPermission方法
@Service public class MyServiceImpl implements MyService { @Override public boolean hasPermission(HttpServletRequest request, Authentication authentication) { //获取主体 Object obj = authentication.getPrincipal(); //判断主体是否属于UserDetails if(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配置文件:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{ //注入异常处理的实现类 @Autowired private 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 @Autowired private PasswordEncoder passwordEncoder; @Override public 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 @Autowired private PasswordEncoder passwordEncoder; @Override public 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 } }
后续内容会继续更新
敬请期待。。。。。。
本人学生党一枚,第一次写博客。如有问题,麻烦各位大哥,高抬贵手,见谅!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)