第三章 权限控制


基于访问路径进行访问权限控制

拦截相应没有访问权限访问页面的用户,注意authorizeRequests与authorizeHttpRequests不能混用,不然报错。

import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class MyWebSecurityConfig {
    @Resource
    private UserDetailsService userDetailsService;

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        //配置web授权访问,"/login","/register","/upLogin"这些统统无需权限
        httpSecurity.authorizeHttpRequests().requestMatchers("/login","register","/upLogin").permitAll()
                //成功的链接,也就是后续访问的链接放在这个里,我这里是成功页需要USER权限才能访问
                .requestMatchers("/success").hasAnyRole("USER").and()
        /**
         * 自定义登陆页面是loginSelf.html页(这里看你模板引擎怎么配),表单提交的处理链接是"/upLogin"
         * 就是<form action="/upLogin"></form>这样,这个"/upLogin"不需要自己处理,就自己决定
         * usernameParameter和passwordParameter,就是表单提交所带的参数,
         * 这里我是"username"和"password";
         */
                .formLogin().loginPage("/loginSelf").loginProcessingUrl("upLogin")
                .usernameParameter("username").passwordParameter("password");
        return httpSecurity.build();
    }

}

以下为新版本


import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.authorizeRequests() //授权请求
//                .anyRequest()//任何请求
//                .denyAll();//拒绝

        http.authorizeRequests()
                //.regexMatchers("/student/**") //这种匹配方式可以不用学
                //.antMatchers("/student/**")  //这种匹配方式可以不用学
                .mvcMatchers("/student/**") //匹配这个url
                //.hasRole("") //是否有单个角色
                //.hasAnyRole("") //是否有任意角色
                //.access("hasAuthority('student:query') or hasRole('admin')") //另外一种权限设置
                .hasAnyAuthority("student:add") //拥有这个权限的用户可以访问这个/student/** ,是否其中至少一个权限

                .mvcMatchers("/teacher/**") //匹配url
                .hasAuthority("ROLE_teacher") //拥有这个权限的用户可以访问/teacher/**
                .anyRequest() //任何请求
                .authenticated(); //都需要登录 ,注意,没有配置mvc的只要登录成功就可以访问

        http.formLogin().permitAll(); //允许表单登录
    }

}

匹配路径url的写法有三种,判断权限有五种。

基于方法来进行访问权限控制

基于方法粒度的权限校验一定要记得在Security配置类中加上@EnableGlobalMethodSecurity注解设置prePostEnabled=true参数,否则直接使用注解配置权限将会无效!!!


import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

//@Configuration 这里不需要加Configuration是因为下面这个注解已经带有Config注解了
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启全局方法安全
public class SecurityUserConfig {

    @Bean
    public UserDetailsService userDetailsService(){
        UserDetails u1 = User.builder()
                .username("cbq1")
                .password(passwordEncoder().encode("123456"))
                .roles("student")
                .build();

        UserDetails u2 = User.builder()
                .username("cbq2")
                .password(passwordEncoder().encode("123456"))
                .authorities("teacher:update","teacher:query")
                .build();

        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(u1);
        inMemoryUserDetailsManager.createUser(u2);
        return inMemoryUserDetailsManager;
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

配置完之后即可使用了:


import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("teacher")
public class TeacherController {

    @GetMapping("add")
    @PreAuthorize("hasRole('teacher')") //预授权
    public Object add(){
        return "添加成功";
    }

    @GetMapping("delete")
    @PreAuthorize("hasRole('teacher')")
    public Object delete(){
        return "删除成功";
    }

    @GetMapping("update")
    @PreAuthorize("hasRole('teacher') or hasAuthority('teacher:update')")
    public Object update(){
        return "更新成功";
    }

}

第四章 利用处理器返回JSON

设置登录成功返回json

设置登录成功返回json创建一个类并且实现AuthenticationSuccessHandler接口,


import com.example.springsecuritystudy3.vo.HttpResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @description: 只要认证成功那么久返回json
 * @author 长白崎
 * @date 2023/7/17 5:11
 * @version 1.0
 */
@Slf4j
@Component
public class AppAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Resource
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        HttpResult httpResult = HttpResult.builder()
                .code(1)
                .msg("登录成功")
                .build();

        String responseJson = objectMapper.writeValueAsString(httpResult);

        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.println(responseJson);
        writer.flush();
    }
}

实现接口之后只需要Security配置类当中配置好即可。


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import javax.annotation.Resource;

@Configuration
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    AppAuthenticationSuccessHandler appAuthenticationSuccessHandler;

    @Resource
    AppAuthenticationFailureHandler appAuthenticationFailureHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated();

        //设置登录成功处理器
        http.formLogin()
                .successHandler(appAuthenticationSuccessHandler) //配置登录成功事件
                .failureHandler(appAuthenticationFailureHandler) //配置登录失败事件
                .permitAll();
    }
}