SpringSecurity系列——访问权限判断(权限,角色,IP),权限不足(403)处理day6-1(源于官网5.7.2版本)

250 阅读3分钟

SpringSecurity系列——访问权限判断(权限,角色,IP),权限不足(403)处理day6-1(源于官网5.7.2版本)

前言

为了你对本文的内容不产生疑惑请先看下方说明

说明

关于本文的测试用户

本文测试用户为:

  1. 用户名:user1,密码:123,角色信息:user,admin,权限信息:ROLE_user, ROLE_admin
  2. 用户名:user2,密码:456,角色信息:guest,权限信息:ROLE_guest

关于本文的其他类

在这里插入图片描述

AuthenticationEvents

package com.example.test1.config;

import org.springframework.context.event.EventListener;
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

@Component
public class AuthenticationEvents {

    @EventListener
    public void InteractiveSuccess(InteractiveAuthenticationSuccessEvent event){
        System.out.println("interactive success");
        System.out.println(event.getAuthentication().getAuthorities());
        System.out.println(event.getAuthentication().getPrincipal());
//        System.out.println(event.getAuthentication());
    }

    @EventListener
    public void onFailure(AbstractAuthenticationFailureEvent failureEvent){
        System.out.println("fail....");
        System.out.println(failureEvent.getAuthentication());
        System.out.println(failureEvent.getException());
    }

DefineLogoutSuccessHandler

package com.example.test1.config;


import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;

public class DefineLogoutSuccessHandler implements LogoutSuccessHandler {


    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        System.out.println("=============logout handler==============");
        System.out.println(request.getMethod());
        System.out.println(authentication.getPrincipal());
        HashMap<String, Object> data = new HashMap<>();
        data.put("status",200);
        data.put("msg","退出登录成功");
        data.put("userData",authentication.getPrincipal());
        response.setContentType("application/json;charset=UTF-8");
        String s = new ObjectMapper().writeValueAsString(data);
        response.getWriter().println(s);
    }
}

其余类的文章地址

点击访问,查看其余类

基于权限的控制

只要在SpringSecurity中进行hasAuthority或hasAnyAuthority配置即可,具体写法如下

关键配置

hasAuthority单个权限控制

http.authorizeRequests(
                        auth -> 
                        auth.antMatchers("/test").hasAuthority("ROLE_admin")
                )

hasAnyAuthority多个权限控制中任意一个

http.authorizeRequests(
                        auth ->
                        .antMatchers("/test3").hasAnyAuthority("ROLE_admin","ROLE_user")
                )

基于角色的控制

只要在SpringSecurity中进行hasRole或hasAnyRole配置即可,具体写法如下

关键配置

hasRole单个角色控制

http.authorizeRequests(
                        auth -> 
                        auth.antMatchers("/test2").hasRole("admin")
                )

hasAnyRole多个角色控制

http.authorizeRequests(
                        auth -> 
                        auth.antMatchers("/test4").hasAnyRole("admin","user")
                )

基于IP的控制

我们使用hasIpAddress方法进行配置

注意点

==本机地址localhost,127.0.0.1本机回环地址,本机地址这三个是不一样的!== 根据我测试下来情况如下

1. 设置hasIpAddress("localhost")

  1. 直接访问127.0.0.1:8080/test5无需认证
  2. 直接访问localhost:8080/test5无需认证
  3. 直接访问192.168.28.159/test5需要认证

2. 设置hasIpAddress("127.0.0.1")

  1. 直接访问127.0.0.1:8080/test5无需认证
  2. 直接访问localhost:8080/test5无需认证
  3. 直接访问192.168.28.159/test5需要认证

3. 设置hasIpAddress("192.168.28.159")

  1. 直接访问127.0.0.1:8080/test5需要认证
  2. 直接访问localhost:8080/test5需要认证
  3. 直接访问192.168.28.159/test5无需认证

关键配置

http.authorizeRequests(
                        auth -> 
                        auth.antMatchers("/test5").hasIpAddress("127.0.0.1")
                )

测试结果说明

测试结果可想而知,user1是全部可以访问的,user2则是全部不能访问 当我看到如下错误的时候看到一个状态=403说明就是权限不够了 在这里插入图片描述

自定义403处理

当我们无权限时,SpringSecurity会给我们返回403的空白页面,实际上对用户是不友好的,用户不关心处理结果,只关心自己能否访问,所以自定义权限不足的时候的处理显得十分重要,在前后端分离的今天,其实我们只需要返回给前端json数据即可

实例代码

1.SpringSecurity中添加配置

关键配置

    private final AuthenicationDeniedHandler deniedHandler;

    @Autowired
    public SpringSecurityConfig( AuthenicationDeniedHandler deniedHandler) {
       
        this.deniedHandler = deniedHandler;
    }

http.exceptionHandling().accessDeniedHandler(deniedHandler);

完整配置

package com.example.test1.config;


import com.example.test1.filter.LoginFilter;
import com.example.test1.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.config.Customizer;

import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;

@EnableWebSecurity
@Configuration
public class SpringSecurityConfig {


    private final UserDetailsServiceImpl userDetailsService;
    private final AuthenicationDeniedHandler deniedHandler;

    @Autowired
    public SpringSecurityConfig(UserDetailsServiceImpl userDetailsService, AuthenicationDeniedHandler deniedHandler) {
        this.userDetailsService = userDetailsService;
        this.deniedHandler = deniedHandler;
    }


    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        AuthenticationManager authenticationManager = configuration.getAuthenticationManager();
        System.out.println(authenticationManager);
        return configuration.getAuthenticationManager();
    }

    //身份验证事件,发布 AuthenticationEventPublisher
    @Bean
    public AuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        return new DefaultAuthenticationEventPublisher(applicationEventPublisher);
    }

    //认证事件
    @Bean
    public AuthorizationEventPublisher authorizationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        return new SpringAuthorizationEventPublisher(applicationEventPublisher);
    }


//    @Bean
//    public LoginFilter loginFilter(){
//        LoginFilter loginFilter = new LoginFilter();
//        loginFilter.setUsernameParameter("username");
//        loginFilter.setPasswordParameter("password");
//        return loginFilter;
//    }


    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests(
                        auth -> auth.antMatchers("/test").hasAuthority("ROLE_admin")
                                .antMatchers("/test2").hasRole("admin")
                                .antMatchers("/test3").hasAnyAuthority("ROLE_admin", "ROLE_user")
                                .antMatchers("/test4").hasAnyRole("admin", "user")
                                .antMatchers("/test5").hasIpAddress("192.168.31.149")
                                .anyRequest().authenticated()

                )
                .formLogin(Customizer.withDefaults())
//                .and()
//                .addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class)
                .httpBasic(Customizer.withDefaults())
                .logout(
                        logout ->
                                //自定义logout处理
                                logout.logoutSuccessHandler(new DefineLogoutSuccessHandler())
                                        .invalidateHttpSession(true)


                )
                .userDetailsService(userDetailsService)
                .exceptionHandling().accessDeniedHandler(deniedHandler);//异常处理
        return http.build();
    }


}

2.自定义处理

package com.example.test1.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;

@Component
public class AuthenicationDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        System.out.println("========== access deny handler =================");
        System.out.println(accessDeniedException.getMessage());
        HashMap<String, Object> data = new HashMap<>();
        data.put("status",403);
        data.put("msg","用户权限不足!");
        response.setContentType("application/json; charset=UTF-8");
        String s = new ObjectMapper().writeValueAsString(data);
        response.getWriter().println(s);
        response.getWriter().flush();
        response.getWriter().close();
    }
}

3.测试

在这里插入图片描述

在这里插入图片描述