A092_spring_security.md


springsecurity框架学习

1. springsecurity小试牛刀

1.1 案例-01 实现访问任何资源都要认证
1.1.1 创建一个普通的maven工程
  • 工程名:spring-security-demo(很普通的maven工程)
1.1.2 完善工程所需的依赖环境
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 下面的坐标就是 spring-security所需的依赖包-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>
1.1.3 配置yml文件
server:
  port: 8888
1.1.4 准备初始案例代码
  • 创建一个controller类,并添加一个普遍的接口方法
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyTestContrl {

    @GetMapping("/index")
    public String index(){
        return "欢迎来到security框架的世界";
    }
}
  • 添加启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SecurityMainApp {
    public static void main(String[] args) {
        SpringApplication.run(SecurityMainApp.class, args);
    }
}
1.1.5 最终在浏览器中,访问地址,显示的结果
http://localhost:8888/index

在这里插入图片描述

为什么在添加了 springsecurity坐标后,什么都不做,再访问controller中的方法时,就会直接跳转到 一个登录页面???
	理由:因为导入的pom依赖是:spring-boot-starter-security。这个坐标已经具备了最基础的认证+授权的配置。
1.2 案例-02 实现指定登录帐号和密码效果
  • 实现指定登录帐号和密码进行认证的效果,重点是需要对spring-security框架进行配置

springsecurity框架在使用中,有一个非常重要的配置文件,有关它的配置,将全部在这个文件中完成

  • 在工程中,创建一个配置文件类,名称定义为 WebSecurityConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration //它是一个配置文件类 --- 类似于以前的xml配置文件
@EnableWebSecurity // 开启springsecurity的web权限控制
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean  //提供用户信息,这里没有从数据库查询用户信息,在内存中模拟
    public UserDetailsService userDetailsService(){
        // 创建一个提供用户信息的接口实现类,这个实现类是基于 InMemory  基于内存
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        //往基于内存的 用户信息提供类中,添加了一条测试数据
        inMemoryUserDetailsManager.createUser(User.withUsername("zs").password("123").authorities("admin").build());
        return inMemoryUserDetailsManager;
    }
  
    @Bean //密码编码器:不加密
    public PasswordEncoder passwordEncoder(){//PasswordEncoder 它指的 密码加密的方式
        return NoOpPasswordEncoder.getInstance();
    }
}
  • 实现的效果就是:访问资源时,会先跳转到登录,输入配置文件中的测试帐号后,登录成功就跳转到 访问controller方法

2. springsecurity 登堂入室

  • 关于springsecurity进行认证时的核心流程图

在这里插入图片描述

传送的核心思想:上面流程图中,每一个Filter都对应着一个具体的功能
  • 关于spring-security执行认证的详细流程图

在这里插入图片描述

2.1 实现连接自己数据库完成认证
  • 实现方式:实现springsecurity的接口UserDetailService
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;

@Service //交给spring创建
public class UserDetailServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //在这里来添加一个测试的用户信息
        ArrayList<GrantedAuthority> authorities = new ArrayList<>();
        //添加一个测试角色,角色名叫 :admin
        //添加权限时,API中提供的方法,是需要传入的是角色名称。注意:这里我们也可以传入用户的权限列表。
        //传入权限列表与角色的区别:权限控制的深度
        authorities.add(new SimpleGrantedAuthority("admin"));

        return new User("admin", "123", authorities);
    }
}
2.2 进行数据库连接后的认证
2.2.1 准备数据库sql
  • 从提供的资源文件中,导入security.sql脚本。数据库的名称:hrm-security
2.2.2 使用mybatis-plus的代码生成器生成表对应的代码
  • 目前需要使用到的表主要是5张
    t_role
    t_user_role
    t_vip_user
    t_permission
    t_role_permission
2.2.3 导入连接数据库所需的pom依赖
<dependency>
    <groupId>itsource.cw</groupId>
    <artifactId>hrm-basic-util</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<!-- swagger-ui的依赖-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springsecurity权限框架的环境依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>


<!-- 导入mybatis-plus相关的依赖-->
<!-- 导入数据库连接相关的依赖-->
<!-- 数据库驱动和druid-->
<!-- 注意坑: mybatis-plus3.x后,在自动生成的实体类中,对日期类型进行了升级,使用的是jdk1.8的LocalDate
     要支持这种数据类型,必须对连接池druid和mysql驱动进行升级,升级版本如下:
     -->
<!-- MySql -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.17</version>
</dependency>
<!-- druid连接池  这个starter的坐标实际上也只是导入了关联版本的druid连接池坐标,所以这里直接用导入druid坐标替代-->
<!--<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.1</version>
</dependency>
<!-- mybatis-plus3的依赖-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.0</version>
</dependency>
2.2.4 添加yml的配置
#配置数据库的连接
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource # 连接池指定 springboot2.02版本默认使用HikariCP 此处要替换成Druid
    driver-class-name: com.mysql.cj.jdbc.Driver # 这个驱动必须用新版,不能用老版
    url: jdbc:mysql:///hrm-security?characterEncoding=utf-8&serverTimezone=GMT%2B8
    username: root
    password: 123456
    druid:
      initial-size: 5 # 初始化时建立物理连接的个数
      min-idle: 5 # 最小连接池连接数量,最小空闲数量
      max-active: 20 # 最大连接池连接数量,最大活跃连接数
      max-wait: 60000 # 配置获取连接等待超时的时间
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1
      testWhileIdle: true
      testOnBorrow: true
      testOnReturn: false
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      filters: stat,wall
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      stat-view-servlet:
        allow: 0.0.0.0 # 允许哪些IP访问druid监控界面,多个IP以逗号分隔
        login-username: admin # 设置登录帐号
        login-password: 123456 # 设置登录密码
        reset-enable: false # 是否允许重置数据
        # url-pattern: /database/* # 默认访问根路径是:/druid/;也可以自定义设置
mybatis-plus: # mybatis的配置。下面的配置希望执行Sql语句可以打印到 控制台
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2.2.5 改造UserDetailService的接口,根据用户名查询数据库实现认证
import cn.itsource.cw.domain.Permission;
import cn.itsource.cw.domain.VipUser;
import cn.itsource.cw.mapper.VipUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;

@Service
public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    private VipUserMapper vipUserMapper;

    @Override //根据参数中的用户名,来查询用户的对象信息
    //1.整个方法体中,并没有对前端传过来的密码进行校验。
    //2.前端传过来的用户密码,后台代码根本就没有提供获取的方法。
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        VipUser vipUser = vipUserMapper.findUserByUserName(userName);
        //在传入时,会同时查询出当前用户的权限列表
        List<GrantedAuthority> authorityList = new ArrayList<>();
        //在security进行授权管理时,就要在这里将当前登录用户的权限列表给查出来
        List<Permission> permissionList = vipUserMapper.findPermissionListByUserName(userName);
        for (Permission permission : permissionList) {
            //将当前用户的权限列表查出来,并保存到security
            authorityList.add(new SimpleGrantedAuthority(permission.getExpression()));
        }
        //密码最终校验的地方是由Security框架完成。 原始密码保存在SecurityContext上下文,传入一个加密之后的密码:vipUser.getPassword()
        return new User(userName, vipUser.getPassword(), authorityList);
    }
}
  • 注意:当我们自定义了UserDetailService的实现类后,务必要将配置类WebSecurityConfig.java中的`public UserDetailsService userDetailsService()这个方法注释掉。
2.2.6 编写根据用户名查询权限列表的sql语句
  1. 编写对应的mapper.java的查询方法
   List<Permission> findPermissionListByUserName(String userName);
  1. 编写对应的sql语句(写在Mapper.xml文件中)
<select id="findPermissionListByUserName" resultType="itsource.cw.domain.Permission" parameterType="string">
    select  * from t_permission where id in(
        select permission_id from  t_role_permission where role_id in (
            select role_id from t_user_role where user_id in (
                select  id from t_vip_user where  username = #{userName}
                )
        )
    )
</select>
  1. 测试:同样,访问测试的controller方法,根据数据库中保存的用户信息进行登录
    测试时,需要先做一件事:将用户表t_vip_user中的密码,先改成明文,再进行测试
2.2.7 改造WebSercurity的配置,将密码的加密方式改成指定的加密
@Bean //密码编码器
public PasswordEncoder passwordEncoder(){//PasswordEncoder 它指的 密码加密的方式
    //return NoOpPasswordEncoder.getInstance();
    //按照security默认的加密方式进行加密
    return new BCryptPasswordEncoder();
}
  • 对BCryptPasswordEncoder加密方式进行测试学习
@Test
public void test1(){
    //这个工具类中,提供2个核心方法:加密 + 解密
    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    //加密
    String encode = passwordEncoder.encode("123");
    /*
     加密之后的结果:
         $2a$10$UmYgHJIY7uSmd/3HrLAc4e8FcU/uZpgZPInVoJ6Ca0e.MuZNJtu7G
         $2a$10$kbb2wCqCHLJoDTOoXI.tk.wE4dO01z2r2edac8Y79HbgSILmCKm7u
         $2a$10$8y7sS7w02e6j3prhR2r4/OpUrGWucF2Q07AKU0A6dU2c8jyzxXbnO
         */
    System.out.println(encode);
    //解密
    boolean matches = passwordEncoder.matches("123", "$2a$10$UmYgHJIY7uSmd/3HrLAc4e8FcU/uZpgZPInVoJ6Ca0e.MuZNJtu7G");
    System.out.println(matches);
}

结论:security内置的加密方式,对同一个字符串,每次加密,都会产生不同的加密字符串

  • 总结
加密方法:passwordEncoder.encode(原始密码) ----》每次加密,得到的加密字符串可变
校验方法:passwordEncoder.matches(原始密码,加密之后的密码) ----》校验成功,返回true
注意:这种加密,也是不可逆的

3. 授权操作–游刃有余

  • 授权的使用是 基于security的全注解完成
3.1 关于3种注解方式
@Secured :它在使用时,需要对添加的角色权限,添加一个前缀 ROLE_
	使用特点:@Secured(“ROLE_DEPT”) ,需要拥有部门的角色才能访问,ROLE_前缀是固定的
@PreAuthorize : 它在使用时,无需添加任何前缀,直接通过方法调用的方式使用
	使用特点:添加权限时,是通过方法进行操作
@PostAuthorize :这种方式使用较少

重点:掌握 第2种注解的使用
3.2 关于@PreAuthorize的使用流程
3.2.1 开启注解
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启@PreAuthorize注解
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
  • 在springsecurity的配置类上,添加注解@EnableGlobalMethodSecurity(prePostEnabled = true),即可让注解生效。
3.2.2 在控制层添加注解实现权限控制
  • 在控制层代码中,可以对每一个需要权限控制的方法,添加上权限注解
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

@RestController
public class EmployeeController {

    //zs有这个访问权限,但ls没有;所以zs可访问,但ls无法访问,报403权限不足错误
    @GetMapping("/employee/list")
    @PreAuthorize("hasAuthority('employee:list')")
    public String list() {
        return "employee.list";
    }

    @PostMapping("/employee/add")
	//@Secured("ROLE_employee:add")
    @PreAuthorize("hasAuthority('employee:add')")
    public String add() {
        return "employee.add";
    }

    @PostMapping("/employee/update")
    //访问当前访问,只要具备employee:update 或 dept:update 都 可访问
    @PreAuthorize("hasAnyAuthority('employee:update','dept:update')")
    public String update() {
        return "employee.update";
    }

    @DeleteMapping("/employee/delete")
    @PreAuthorize("hasAuthority('employee:delete')")
    public String delete() {
        return "employee.delete";
    }
}
  • 重点关注
@PreAuthorize("hasAuthority('employee:add')") 表示访问指定方法,需要有'employee:add'的权限
@PreAuthorize("hasAnyAuthority('employee:update','dept:update')")
    表示访问指定方法,需要至少具备 'employee:update','dept:update'2个中的任何一个
  • 结论
关于授权的方案,在权限控制的实现过程中,我们全部通过 注解完成,不需要写任何java代码

4. 关于对Security认证及授权的代码优化

4.1 自定义登录界面
  • 自定义登录界面需要对security配置类,进行相应配置
//授权规则配置
    /*
        在配置类中,对各种需要权限控制的资源,进行配置的方式 :
                只有在程序重启之后,才会生效
       注意:在configure方法中,写各项配置,它是有顺序的,写在前端的先生效,写在后面的,如果配置相同,不会生效
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        http.authorizeRequests()                                //授权配置
            // 指定哪些访问的请求地址,不需要认证就可以访问。
                .antMatchers("/login", "/login.html").permitAll()  //登录路径放行
                .anyRequest().authenticated()     //其他路径都要认证之后才能访问
                .and().formLogin()                              //允许表单登录
                .loginPage("/login.html")                        //这个就是自定义的登录页面
                .loginProcessingUrl("/login")                   //告诉框架,现在的登录请求的URL地址是:/login
//                .usernameParameter("abc")
//                .passwordParameter("def")
                .successForwardUrl("/loginSuccess")             // 设置登陆成功页

                .and().logout().permitAll()                    //登出路径放行 /logout。这是框架自带的登出请求
                .and().csrf().disable();                        //关闭跨域伪造检查

    }
  • 注意事项
  1. 自定义界面调用的方法 .loginPage("/login.html")
  2. 如果自定义了登录界面,则需要配置当前请求URL .loginProcessingUrl("/login")
  3. 在登录界面中,如果要将用户的登录帐号和密码正确传给后台的Security接收到 ,参数名不能乱写
默认情况下,框架接收的登录帐号和密码的变量名是:(注意大小写区分)
		登录帐号: username
		登录密码: password
如果需要自定义,则在配置方法上,进行如下配置:	
                .usernameParameter("abc")
                .passwordParameter("def")
  • 创建的登录界面是一个login.html,它必须放在工程的 resources/static目录下
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
<h1>这是自定义的登陆界面</h1>
<h2>注意:当自定义登录界面后,传入的帐号和密码的输入框name的名称不能随便写,只能是 username和password</h2>
<form method="post" action="/login">
    <div>
        用户名:<input type="text" name="username">
    </div>
    <div>
        密码:<input type="password" name="password">
    </div>
    <div class="checkbox"><!-- rememberme的name的名称使用的也是默认的,不能修改 -->
        <label><input type="checkbox" id="rememberme" name="remember-me"/>记住我</label>
    </div>
    <div>
        <button type="submit">立即登陆</button>
    </div>
</form>
</body>
</html>
4.2 解决登录失败,给个友好提示
4.2.1 修改配置类,这个配置方法也是写在 configure()方法体中的
.successHandler(new MyAuthenticationSuccessHandler()) //设置认证成功后,自定义处理
.failureHandler(new MyAuthenticationFailureHandler()) //设置认证失败后,自定义处理
4.2.2 登录成功和失败的自定义方法处理类
  1. MyAuthenticationSuccessHandler.java 成功
import com.alibaba.fastjson.JSON;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

//这个实现类的作用:当security认证成功后,将会执行当前handler处理器
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {
        //这里经常做的事情:当认证成功后,拼接一个JSON格式的数据返回给请求前端
        Map map = new HashMap<>();
        map.put("success",true);
        map.put("message","认证成功");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-type", "text/html; charset=utf-8");
        response.getWriter().print(JSON.toJSONString(map));

        response.getWriter().flush();
        response.getWriter().close();
    }
}
  1. MyAuthenticationFailureHandler.java 失败
import com.alibaba.fastjson.JSON;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        Map map = new HashMap<>();
        map.put("success",false);
        map.put("message","认证失败");
        //在返回前端数据时,还可以设置一个http的状态(类似:404、403等)
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-type", "text/html; charset=utf-8");
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.getWriter().print(JSON.toJSONString(map));
        response.getWriter().flush();
        response.getWriter().close();
    }
}
4.3 访问某个方法时,提示403异常
4.3.1 在配置类中,添加一个自定义403的异常处理
//配置权限不足时,自定义结果返回给前端
http.exceptionHandling().accessDeniedHandler(new DefaultAccessDeniedHandler());
4.3.2 自定义异常处理类
import cn.itsource.cw.util.AjaxResult;
import com.alibaba.fastjson.JSON;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class DefaultAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        String result = JSON.toJSONString(AjaxResult.me().setSuccess(false).setMessage("无访问权限"));
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print(result);
        writer.flush();
        writer.close();
    }
}
4.3.3 关于security最终的完整配置类代码如下
@Override
protected void configure(HttpSecurity http) throws Exception {
    System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
    http.authorizeRequests()                                //授权配置
        .antMatchers("/login", "/login.html").permitAll()  //登录路径放行
        //进行授权的配置:当要访问资源【/employee/list】,需要当前用户具备权限:employee:list
        //现在数据库中,只有zs才有这个权限。最终的效果:张三能正常访问,但李四不可以
        //配置访问的资源对应的权限配置,必须要放在.anyRequest().authenticated() 之前
        //在配置类中,针对请求的方法进行权限控制:它是全局的。是程序在启动时就已经加载。
        //总结:所有资源权限控制,都必须要在这里配置,才能实现全局控制。
        // 问题:所有开发人员,要对自己的资源进行控制,都必须改这个配置类,不安全,容易改错。
        //                .antMatchers("/employee/list/**").hasAuthority("employee:list")
        .anyRequest().authenticated()     //其他路径都要认证之后才能访问
        .and().formLogin()                              //允许表单登录
        .loginPage("/login.html")                        //这个就是自定义的登录页面
        .loginProcessingUrl("/login")    //告诉框架,现在的登录请求的URL地址是:/login
        //                .usernameParameter("abc")
        //                .passwordParameter("def")
        .successForwardUrl("/loginSuccess")             // 设置登陆成功页
        .successHandler(new MyAuthenticationSuccessHandler())//设置认证成功后,handler的处理器
        .failureHandler(new MyAuthenticationFailureHandler())//设置认证失败后handler的处理器
        .and().logout().permitAll()     //登出路径放行 /logout。这是框架自带的登出请求
        .and().csrf().disable();                        //关闭跨域伪造检查
    //        配置权限不足时,自定义结果返回给前端
    http.exceptionHandling().accessDeniedHandler(new DefaultAccessDeniedHandler());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值