springsecurity介绍和入门案例

一.springsecurity简单介绍

1.SpringSecurity基于Spring框架,为web应用中的权限控制提供了一套解决方案。使用springsecurity可以简化系统中权限控制相关功能的开发

二.普通例子

2.1创建springboot工程名为SpringBoot_Security

1)配置Maven

2)导入web的starter,以下为pom文件部分代码

  <parent>
    <artifactId>spring-boot-starter-parent</artifactId>
    <groupId>org.springframework.boot</groupId>
    <version>2.4.5</version>
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  </dependencies>

2.2在java目录下创建包为com/hhh/controller,其包下创建类为ResourcesController

package com.hhh.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/res")
public class ResourcesController {
    @GetMapping("/res1")
    public String res1(){
        return "res1===>";
    }


    @GetMapping("/res2")
    public  String res2(){
        return "res2===>";
    }
}

2.3在resources目录下创建配置文件application.properties

server.port=8081

2.4在com/hhh包下创建启动类AppApplication

package com.hhh;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

//启动类
@SpringBootApplication
public class AppApplication {
    public static void main(String[] args) {
        SpringApplication.run(AppApplication.class,args);
    }
}

2.5启动工程,在浏览器中访问,查看效果

三.入门案例

采用springboot工程来整合spring、,security,可以简化开发步骤

3.1导入spring-security的starter

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
      <!--已经通过上边的父工程指定了版本号-->
    </dependency>

刷新工程

 3.2这时重新启动工程再次访问就有权限控制

 3.3控制台有显示密码,默认的用户名为user

3.4输入正确用户名和密码后即可访问成功 

 四.修改用户名和密码

4.1在内存中配置一个用户,这里在配置类中配置

1)新建一个包com/hhh/config,其包下新建配置类SecurityConfig

package com.hhh.config;

import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.stereotype.Controller;

@Controller
public class SecurityConfig {
    //在内存中配置一个用户
    @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager manager=new InMemoryUserDetailsManager();
     /*用户名为aaa,密码123456,权限为p1*/   

manager.createUser(User.withUsername("aaa").password("123456").authorities("p1").build());
        return  manager;
    }
/*配置编码器,框架要求必须有编码器,这里做的编码器不做编码*/
    @Bean
    public PasswordEncoder passwordEncoder(){
        //这个编码器相当于不做编码
        return NoOpPasswordEncoder.getInstance();
    }

}

2)重新启动工程,按照配置的用户名和密码,即可访问成功

 3)访问成功

 4.2UserDetailService

UserDetailService是一个接口,springsecurity通过这个接口来查找用户信息,和前端传递过来的用户信息进行比较

五.springsecurity的安全配置

1.直接访问登录页面(localhost:8081/index.html),如果工程中没有静态资源存放的文件的index.html文件,报404

 2.在webapp目录下新建文件index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>

3.直接访问登录页面

登录成功后会跳转到首页(index.html)

 4.springsecurity的安全配置

通过在配置类中继承WebSecurityConfigureAdapter类,重写其中的configure(HttpSecurity http)方法来调整调整springsecurity的配置

4.1SecurityConfig新添代码

package com.hhh.config;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.stereotype.Controller;

@Controller
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //在内存中配置一个用户
    @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager manager=new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("aaa").password("123456").authorities("p1").build());
        return  manager;
    }
/*配置编码器,框架要求必须有编码器,这里做的编码器不做编码*/
    @Bean
    public PasswordEncoder passwordEncoder(){
        //这个编码器相当于不做编码
        return NoOpPasswordEncoder.getInstance();
    }

    /*重写其中的configure(HttpSecurity http)方法来调整调整springsecurity的配置*/

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*访问该路径受权限控制,authenticated()表示登录就能访问*/
       http.authorizeRequests().antMatchers("/res/res1").authenticated();
       /*其他路径放行*/
       http.authorizeRequests().anyRequest().permitAll();
       http.formLogin();

    }
}

4.2启动工程,访问res/res2正常访问

4.3访问res/res1受到权限控制

 4.4输入对应用户名和密码即可访问成功

 4.5配置某些资源需要特定的权限才能访问,SecurityConfig部分代码

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*访问该路径受权限控制*/
       http.authorizeRequests().antMatchers("/res/res1").authenticated();

       /*配置某些资源需要特定的权限才能访问*/
        http.authorizeRequests().antMatchers("/res/res2").hasAuthority("p2");
        /*在上方创建用户时设置了p1权限,这里指定了p2权限,即访问res2失败*/
       /*其他路径放行*/
       http.authorizeRequests().anyRequest().permitAll();

       http.formLogin();

    }

4.6启动工程,访问res2查看效果,报403错误

 4.7可以在配置用户那配置p2权限,这样重新启动工程,res2也可以被访问到

4.8注意:配置路径匹配规则时,越详细的路径配置规则写在前面

因为一次请求匹配到第一个规则后就会忽略后边的规则,如下

@Override
    protected void configure(HttpSecurity http) throws Exception {
        /*模糊匹配路径设置在前头,访问res2就不需要p2权限*/
        http.authorizeRequests().antMatchers("/res/**").authenticated();
        /*访问该路径受权限控制*/
       http.authorizeRequests().antMatchers("/res/res1").authenticated();

       /*配置某些资源需要特定的权限才能访问*/
        http.authorizeRequests().antMatchers("/res/res2").hasAuthority("p2");
        /*在上方创建用户时设置了p1权限,这里指定了p2权限,即访问res2失败*/
       /*其他路径放行*/
       http.authorizeRequests().anyRequest().permitAll();

       http.formLogin();

    }

忽略后面规则,访问成功

 4.9配置退出

1)默认url:/logout 

 2)点击Log Out后回到登录页面

 3)自定义配置,configure方法里的部分代码

  http.csrf().disable();  //关闭csrf

/*
中间路径权限控制
*/

 //配置退出
 http.logout().logoutUrl("/out");

4)启动工程,登录成功后,使用配置的退出即可

5)在webapp目录下新建文件logoutSuccess.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>页面</title>
</head>
<body>
<h1>退出成功</h1>
</body>
</html>

6)配置退出后跳转到其他页面

//配置退出后跳转的页面
http.logout().logoutUrl("/out").logoutSuccessUrl("/logoutSuccess.html");

7)启动工程,登录成功后,退出该页面,查看效果

六.springsecurity自定义登录页面和方法级权限控制

1.在webapp目录下创建文件为login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
    <style>
        .center {
            display: flex;
            width: 350px;
            margin: 0 auto;
            margin-top: 100px;
            height: 220px;
            background-color: blanchedalmond;
        }
        form {
            width: 320px;
            margin: 0 auto;
        }
        .user-name {
            display: flex;
            margin-top: 20px;
            justify-content: space-between;
            align-items: center;
        }
        .user-name input {
            width: 220px;
            height: 30px;
        }
        .password {
            margin-top: 30px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .password input {
            width: 220px;
            height: 30px;
        }
        .btn {
            margin-top: 30px;
            display: flex;
        }
        .btn input {
            width: 320px;
            height: 30px;
        }
    </style>
</head>
<body>
<div class="center">
    <form action="/login" method="post">
        <div class="user-name">
            <div>
                用户名
            </div>
            <div>
                <input type="text" name="username">
            </div>

        </div>
        <div class="password">
            <div>
                密码
            </div>
            <div>
                <input type="password" name="password">
            </div>
        </div>
        <div class="btn">
            <input type="submit" value="提交">
        </div>
    </form>
</div>

</body>
</html>

2.配置自定义登录页面,以部分代码下为SecurityConfig类中configure方法中的部分代码

 /*配置自定义登录页面,前面为静态资源的存放路径,后面为登录提交到哪*/
 http.formLogin().loginPage("/login.html").loginProcessingUrl("/login");
 /*注意配置登录页面(login.html)里的action属性和method属性*/

3.启动工程访问res1,查看效果

 4.输入正确的用户名和密码正常访问到res1,表明设置成功

 5.方法级权限控制

5.1在ResourcesController类中新添一个方法res3

    @GetMapping("/res3")
    public  String res3(){
        return "res3===>";
    }

让res3这个方法受到p1的权限控制有两种方法:

第一,在SecurityConfig类中configure方法中添加对应规则

5.2新建包com/service,其包下新建类为ResourceService

package com.hhh.service;

import org.springframework.stereotype.Service;

@Service
public class ResourceService {
    public String res3(){
        return "success";
    }
}

5.3在ResourcesController类中注入ResourceService并在res3方法中返回该类中的res3方法,以下为ResourcesController的部分代码

@Autowired
    private ResourceService resourceService;
  @GetMapping("/res3")
    public  String res3(){
        return resourceService.res3();
    }

5.4这里控制ResourceService类中的res3方法,让其有p1的权限才能份访问

做法:在安全配置类(SecurityConfig上)加上注解@EnableGlobalMethodSecurity开启方法权限控制

 5.5在需要进行权限控制的方法加上 注解@PreAuthorize("hasAuthority('p1')")

package com.hhh.service;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class ResourceService {
    //要有p1权限才能访问
    @PreAuthorize("hasAuthority('p1')")
    public String res3(){
        return "success";
    }
}

5.6启动工程,访问res3,受到控制不能直接访问

5.7填写正确用户名和密码即可访问成功

 七.springsecurity配置从数据库获取用户

1.导入mybatis,mysql驱动的依赖并刷新依赖

  <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.1.4</version>
  </dependency>
  <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.28</version>
  </dependency>

2.在resources目录下新建包为com/hhh/mapper,其下新建文件UserDao.xml,用来写sql语句,这里先写一个框架,具体sql后续写上

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">


</mapper>

3.将数据库的连接信息在配置文件(application.properties)中配置上

#端口设置
server.port=8081

#数据库连接设置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ssmframe
spring.datasource.username=root
spring.datasource.password=hyt123456


#别名设置和到sql文件扫描路径的配置
mybatis.type-aliases-package=com.hhh.domain
mybatis.mapper-locations=classpath:com/hhh/mapper/*.xml

4.新建包为com/hhh/domain,其包下新建类UserInfo,用来映射用户信息的

package com.hhh.domain;

public class UserInfo {
    private  int id;
    private  String username;
    private  String passwd;
    //权限
    private  String auth;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

    public String getAuth() {
        return auth;
    }

    public void setAuth(String auth) {
        this.auth = auth;
    }
    
}

5.在数据库环境中创建数据库

use ssmframe;
create table t_user(
id int primary key auto_increment,
username varchar(30),
passwd varchar(128),
auth varchar(128)
);

insert into t_user(username,passwd,auth)values
('aaaaa','123456','p1,p2');

select * from t_user;

6.在service层新建类为MyUserDetailsService,实现接口UserDetailsService

package com.hhh.service;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class MyUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //查询数据库,获取用户信息
        return null;
    }
}

7.新建包com/hhh/dao,其包下新建接口UserDao

package com.hhh.dao;

import com.hhh.domain.UserInfo;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserDao {
    public UserInfo findByUsername(String username);

}

8.完善UserDao.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--dao层接口的全路径-->
<mapper namespace="com.hhh.dao.UserDao">
<!--id值为UserDao类中的方法名,参数类型和返回值类型-->
    <select id="findByUsername" parameterType="String" resultType="userInfo">
        select * from t_user where username=#{username};
    </select>

</mapper>

9.继续处理MyUserDetailsService

package com.hhh.service;

import com.hhh.dao.UserDao;
import com.hhh.domain.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
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;

@Service
public class MyUserDetailsService implements UserDetailsService {
    //用到dao层接口,注入
    @Autowired
    private UserDao userDao;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //查询数据库,获取用户信息
        UserInfo userInfo = userDao.findByUsername("username");
        String auth=userInfo.getAuth(); //得到权限
        String[] split = auth.split(",");//用逗号分割,得到一个数组
        //数据库存的是逗号隔开去存的 ,这里是单个存的
        return     User.withUsername(userInfo.getUsername())
                       .password(userInfo.getPasswd())
                       .authorities(split)
                       .build();



    }
}

10.注释SecurityConfig类中配置的用户,启动工程,访问res1查看效果

11.输入从数据库中设置的用户名和密码 ,查看效果(登录失败)

加断点调试

 八.springsecurity对用密码进行加密

1.在SecurityConfig类中进行对密码加密

2.在com/hhh包下创建测试类Test,用来测试这个接口(PasswordEncoder)如何使用

package com.hhh;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class Test {
    public static void main(String[] args) {
        BCryptPasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
        String password="123";
        //密文
        String encode = passwordEncoder.encode(password);
        System.out.println(encode);
    }
}

3.运行这个程序,在控制台中查看

 4.查看匹配机制

package com.hhh;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class Test {
    public static void main(String[] args) {
        BCryptPasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
        String password="123";
        //密文
        String encode = passwordEncoder.encode(password);
        System.out.println(encode);

        //匹配,同样发明文通过计算是不一样的,但是matches方法传的值是第一次或第二次都是true
        //这里数据库中就可以更改密码为密文
        boolean matches = passwordEncoder.matches(password, encode);
        System.out.println(matches);
    }
}

5.更改数据库中数据表t_user中设置的passwd

 6.SecurityConfig类中返回密文的编码器

 7.启动工程,登录res1,输入正确用户名和密码即可登录成功,而此时数据库密码存着是密文

九.springsecurity实现下次自动登录功能

原理:勾选“记住我”复选框,登录成功后,springsecurity会创建出一个token字符串和当前用户关联,保存这个管理关系;根据token创建一个cookie发送给客户端。客户端下次访问时如果携带了这个cookie,security就会验证这个cookie,验证成功就会完成自动登录。

1.添加依赖

  <!--新添依赖,用来实现自动登录-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

2.前端页面login.html中新添复选框

 3.在内存里保存这个关联关系,在SecurityConfig类中操作

        //这里的isRemember与login.html中复选框的name属性值一致,后面为有效期,单位为秒
        http.rememberMe().rememberMeParameter("isRemember").tokenValiditySeconds(30);

4.启动工程,登录res1,查看效果

5.在数据库里保存这个关联关系,在SecurityConfig类中操作


        //在数据库中存关联关系
        http.rememberMe().rememberMeParameter("isRemember")
                .tokenValiditySeconds(30)
                .tokenRepository(jdbcTokenRepository());

    }
    //注入连接池对象
    @Autowired
    private DataSource dataSource;

    public PersistentTokenRepository jdbcTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository=new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        //启动的时候要不要自己创建表,名为persistent_logins
        tokenRepository.setCreateTableOnStartup(true);
        //没有创建表时,使用true,第二次启动工程时记得改为false
        return  tokenRepository;
    }

6.启动工程,登录res1,查看效果

十.springsecurity认证原理分析

spring security是基于过滤器来实现认证和授权的

1.入口过滤器

FilterChainProxy这个过滤器中维护了一个过滤器链,请求到来时,会依次使用过滤器链中的过滤器对请求进行处理
private List<SecurityFilterChain> filterChains;

这个SecurityFilterChain接口用来封装过滤器列表
public interface SecurityFilterChain {

    boolean matches(HttpServletRequest request);

    List<Filter> getFilters();
}
在FilterChainProxy的doFilter方法中会使用从SecurityFilterChain中获取到的过滤器来对请求进行处理。
通过内部类 VirtualFilterChain来实现多个过滤器依次处理请求。

2.过滤器链中重要的过滤器介绍


(1)UsernamePasswordAuthenticationFilter  用来处理表单登录的过滤器
    表单登录的过程:
    a.用户提交用户名和密码,被此过滤器拦截到,将用户信息封装为接口Authentication 对象,
    实际是UsernamePasswordAuthenticationToken这个实现类的对象
    b.将Authentication对象提交到认证管理器AuthenticationManager进行认证
    c.认证成功后AuthenticationManager会返回一个填充了用户信息的Authentication对象。
    d.SecurityContextHolder将设置了用户信息的Authentication对象设置到其内部。
认证管理器:    
AuthenticationManager:
    实现类:ProviderManager
        其中维护了AuthenticationProvider列表,请求到来时会循环维护的这个列表,选择一个可用的Provider进行认证
    
            AuthenticationProvider也是一个接口,实现类:DaoAuthenticationProvider,用来处理从数据库获取用户进行认证的情况
            
            
(2)RememberMeAuthenticationFilter:处理通过记住我进行登录的请求


十一.前后端分离下使用spring security

1.前后端分离开发的售后,前端通过ajax技术调用后端接口,ajax不能处理后端返回的重定向请求,轻易需要调整springsecurity的配置,让后端给前端返回json数据,前端自己判断认证的结果进行路由跳转。

2.在工程中webapp目录下添加测试文件ajaxTest.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/axios.js"></script>
    <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
    <button @click="res1">请求res1</button>
    <button @click="res2">请求res2</button>
    <hr>
    <textarea v-model="resultData"></textarea>
</div>

<script>
    var app = new Vue({
          el: '#app',
          data: {
            message: 'Hello Vue!',
            resultData:''
          },
          created(){

          },
          methods:{
            res2(){
                axios.post('/res/res2').then(res=>{
                   if(res.data.code=='401'){
                    //前端控制跳转逻辑
                    alert('未登录')
                    //location.href='http://localhost/login.html'
                    }else if(res.data.code=='403'){
                        alert("无权限访问");
                    }else{
                        console.log(res.data.data);
                        this.resultData=res.data
                    }
                })
            },
            res1(){
                axios.get('/res/res1').then(res=>{
                    if(res.data.code=='401'){
                    //前端控制跳转逻辑
                    //location.href='http://localhost/login.html'
                    alert('未登录');
                    }else if(res.data.code=='403'){
                        alert("无权限访问");
                    }else{
                        this.resultData=res.data
                    }
                })
            }
          }
    });
</script>
</body>
</html>

3.在webapp下新建文件夹为js,里面新建放置两个ajax的库和前端框架的文件

3.1axios.js

/* axios v0.25.0 | (c) 2022 by Matt Zabriskie */
/* axios v0.24.0 | (c) 2022 by Matt Zabriskie */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=11)}([function(e,t,n){"use strict";var r=n(3),o=Object.prototype.toString;function i(e){return"[object Array]"===o.call(e)}function s(e){return void 0===e}function a(e){return null!==e&&"object"==typeof e}function u(e){if("[object Object]"!==o.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function c(e){return"[object Function]"===o.call(e)}function f(e,t){if(null!=e)if("object"!=typeof e&&(e=[e]),i(e))for(var n=0,r=e.length;n<r;n++)t.call(null,e[n],n,e);else for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&t.call(null,e[o],o,e)}e.exports={isArray:i,isArrayBuffer:function(e){return"[object ArrayBuffer]"===o.call(e)},isBuffer:function(e){return null!==e&&!s(e)&&null!==e.constructor&&!s(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)},isFormData:function(e){return"undefined"!=typeof FormData&&e instanceof FormData},isArrayBufferView:function(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer},isString:function(e){return"string"==typeof e},isNumber:function(e){return"number"==typeof e},isObject:a,isPlainObject:u,isUndefined:s,isDate:function(e){return"[object Date]"===o.call(e)},isFile:function(e){return"[object File]"===o.call(e)},isBlob:function(e){return"[object Blob]"===o.call(e)},isFunction:c,isStream:function(e){return a(e)&&c(e.pipe)},isURLSearchParams:function(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams},isStandardBrowserEnv:function(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)},forEach:f,merge:function e(){var t={};function n(n,r){u(t[r])&&u(n)?t[r]=e(t[r],n):u(n)?t[r]=e({},n):i(n)?t[r]=n.slice():t[r]=n}for(var r=0,o=arguments.length;r<o;r++)f(arguments[r],n);return t},extend:function(e,t,n){return f(t,(function(t,o){e[o]=n&&"function"==typeof t?r(t,n):t})),e},trim:function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")},stripBOM:function(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e}}},function(e,t,n){"use strict";var r=n(0),o=n(17),i=n(5),s={"Content-Type":"application/x-www-form-urlencoded"};function a(e,t){!r.isUndefined(e)&&r.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}var u,c={transitional:{silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},adapter:(("undefined"!=typeof XMLHttpRequest||"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process))&&(u=n(6)),u),transformRequest:[function(e,t){return o(t,"Accept"),o(t,"Content-Type"),r.isFormData(e)||r.isArrayBuffer(e)||r.isBuffer(e)||r.isStream(e)||r.isFile(e)||r.isBlob(e)?e:r.isArrayBufferView(e)?e.buffer:r.isURLSearchParams(e)?(a(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):r.isObject(e)||t&&"application/json"===t["Content-Type"]?(a(t,"application/json"),function(e,t,n){if(r.isString(e))try{return(t||JSON.parse)(e),r.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(n||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||c.transitional,n=t&&t.silentJSONParsing,o=t&&t.forcedJSONParsing,s=!n&&"json"===this.responseType;if(s||o&&r.isString(e)&&e.length)try{return JSON.parse(e)}catch(e){if(s){if("SyntaxError"===e.name)throw i(e,this,"E_JSON_PARSE");throw e}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};r.forEach(["delete","get","head"],(function(e){c.headers[e]={}})),r.forEach(["post","put","patch"],(function(e){c.headers[e]=r.merge(s)})),e.exports=c},function(e,t,n){"use strict";function r(e){this.message=e}r.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},r.prototype.__CANCEL__=!0,e.exports=r},function(e,t,n){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t,n){"use strict";var r=n(0);function o(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}e.exports=function(e,t,n){if(!t)return e;var i;if(n)i=n(t);else if(r.isURLSearchParams(t))i=t.toString();else{var s=[];r.forEach(t,(function(e,t){null!=e&&(r.isArray(e)?t+="[]":e=[e],r.forEach(e,(function(e){r.isDate(e)?e=e.toISOString():r.isObject(e)&&(e=JSON.stringify(e)),s.push(o(t)+"="+o(e))})))})),i=s.join("&")}if(i){var a=e.indexOf("#");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf("?")?"?":"&")+i}return e}},function(e,t,n){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code,status:this.response&&this.response.status?this.response.status:null}},e}},function(e,t,n){"use strict";var r=n(0),o=n(18),i=n(19),s=n(4),a=n(20),u=n(23),c=n(24),f=n(7),l=n(1),p=n(2);e.exports=function(e){return new Promise((function(t,n){var d,h=e.data,m=e.headers,v=e.responseType;function y(){e.cancelToken&&e.cancelToken.unsubscribe(d),e.signal&&e.signal.removeEventListener("abort",d)}r.isFormData(h)&&delete m["Content-Type"];var g=new XMLHttpRequest;if(e.auth){var b=e.auth.username||"",x=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";m.Authorization="Basic "+btoa(b+":"+x)}var w=a(e.baseURL,e.url);function E(){if(g){var r="getAllResponseHeaders"in g?u(g.getAllResponseHeaders()):null,i={data:v&&"text"!==v&&"json"!==v?g.response:g.responseText,status:g.status,statusText:g.statusText,headers:r,config:e,request:g};o((function(e){t(e),y()}),(function(e){n(e),y()}),i),g=null}}if(g.open(e.method.toUpperCase(),s(w,e.params,e.paramsSerializer),!0),g.timeout=e.timeout,"onloadend"in g?g.onloadend=E:g.onreadystatechange=function(){g&&4===g.readyState&&(0!==g.status||g.responseURL&&0===g.responseURL.indexOf("file:"))&&setTimeout(E)},g.onabort=function(){g&&(n(f("Request aborted",e,"ECONNABORTED",g)),g=null)},g.onerror=function(){n(f("Network Error",e,null,g)),g=null},g.ontimeout=function(){var t=e.timeout?"timeout of "+e.timeout+"ms exceeded":"timeout exceeded",r=e.transitional||l.transitional;e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),n(f(t,e,r.clarifyTimeoutError?"ETIMEDOUT":"ECONNABORTED",g)),g=null},r.isStandardBrowserEnv()){var S=(e.withCredentials||c(w))&&e.xsrfCookieName?i.read(e.xsrfCookieName):void 0;S&&(m[e.xsrfHeaderName]=S)}"setRequestHeader"in g&&r.forEach(m,(function(e,t){void 0===h&&"content-type"===t.toLowerCase()?delete m[t]:g.setRequestHeader(t,e)})),r.isUndefined(e.withCredentials)||(g.withCredentials=!!e.withCredentials),v&&"json"!==v&&(g.responseType=e.responseType),"function"==typeof e.onDownloadProgress&&g.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&g.upload&&g.upload.addEventListener("progress",e.onUploadProgress),(e.cancelToken||e.signal)&&(d=function(e){g&&(n(!e||e&&e.type?new p("canceled"):e),g.abort(),g=null)},e.cancelToken&&e.cancelToken.subscribe(d),e.signal&&(e.signal.aborted?d():e.signal.addEventListener("abort",d))),h||(h=null),g.send(h)}))}},function(e,t,n){"use strict";var r=n(5);e.exports=function(e,t,n,o,i){var s=new Error(e);return r(s,t,n,o,i)}},function(e,t,n){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,n){"use strict";var r=n(0);e.exports=function(e,t){t=t||{};var n={};function o(e,t){return r.isPlainObject(e)&&r.isPlainObject(t)?r.merge(e,t):r.isPlainObject(t)?r.merge({},t):r.isArray(t)?t.slice():t}function i(n){return r.isUndefined(t[n])?r.isUndefined(e[n])?void 0:o(void 0,e[n]):o(e[n],t[n])}function s(e){if(!r.isUndefined(t[e]))return o(void 0,t[e])}function a(n){return r.isUndefined(t[n])?r.isUndefined(e[n])?void 0:o(void 0,e[n]):o(void 0,t[n])}function u(n){return n in t?o(e[n],t[n]):n in e?o(void 0,e[n]):void 0}var c={url:s,method:s,data:s,baseURL:a,transformRequest:a,transformResponse:a,paramsSerializer:a,timeout:a,timeoutMessage:a,withCredentials:a,adapter:a,responseType:a,xsrfCookieName:a,xsrfHeaderName:a,onUploadProgress:a,onDownloadProgress:a,decompress:a,maxContentLength:a,maxBodyLength:a,transport:a,httpAgent:a,httpsAgent:a,cancelToken:a,socketPath:a,responseEncoding:a,validateStatus:u};return r.forEach(Object.keys(e).concat(Object.keys(t)),(function(e){var t=c[e]||i,o=t(e);r.isUndefined(o)&&t!==u||(n[e]=o)})),n}},function(e,t){e.exports={version:"0.24.0"}},function(e,t,n){e.exports=n(12)},function(e,t,n){"use strict";var r=n(0),o=n(3),i=n(13),s=n(9);var a=function e(t){var n=new i(t),a=o(i.prototype.request,n);return r.extend(a,i.prototype,n),r.extend(a,n),a.create=function(n){return e(s(t,n))},a}(n(1));a.Axios=i,a.Cancel=n(2),a.CancelToken=n(26),a.isCancel=n(8),a.VERSION=n(10).version,a.all=function(e){return Promise.all(e)},a.spread=n(27),a.isAxiosError=n(28),e.exports=a,e.exports.default=a},function(e,t,n){"use strict";var r=n(0),o=n(4),i=n(14),s=n(15),a=n(9),u=n(25),c=u.validators;function f(e){this.defaults=e,this.interceptors={request:new i,response:new i}}f.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{}).url=arguments[0]:e=e||{},(e=a(this.defaults,e)).method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=e.transitional;void 0!==t&&u.assertOptions(t,{silentJSONParsing:c.transitional(c.boolean),forcedJSONParsing:c.transitional(c.boolean),clarifyTimeoutError:c.transitional(c.boolean)},!1);var n=[],r=!0;this.interceptors.request.forEach((function(t){"function"==typeof t.runWhen&&!1===t.runWhen(e)||(r=r&&t.synchronous,n.unshift(t.fulfilled,t.rejected))}));var o,i=[];if(this.interceptors.response.forEach((function(e){i.push(e.fulfilled,e.rejected)})),!r){var f=[s,void 0];for(Array.prototype.unshift.apply(f,n),f=f.concat(i),o=Promise.resolve(e);f.length;)o=o.then(f.shift(),f.shift());return o}for(var l=e;n.length;){var p=n.shift(),d=n.shift();try{l=p(l)}catch(e){d(e);break}}try{o=s(l)}catch(e){return Promise.reject(e)}for(;i.length;)o=o.then(i.shift(),i.shift());return o},f.prototype.getUri=function(e){return e=a(this.defaults,e),o(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},r.forEach(["delete","get","head","options"],(function(e){f.prototype[e]=function(t,n){return this.request(a(n||{},{method:e,url:t,data:(n||{}).data}))}})),r.forEach(["post","put","patch"],(function(e){f.prototype[e]=function(t,n,r){return this.request(a(r||{},{method:e,url:t,data:n}))}})),e.exports=f},function(e,t,n){"use strict";var r=n(0);function o(){this.handlers=[]}o.prototype.use=function(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!n&&n.synchronous,runWhen:n?n.runWhen:null}),this.handlers.length-1},o.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},o.prototype.forEach=function(e){r.forEach(this.handlers,(function(t){null!==t&&e(t)}))},e.exports=o},function(e,t,n){"use strict";var r=n(0),o=n(16),i=n(8),s=n(1),a=n(2);function u(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new a("canceled")}e.exports=function(e){return u(e),e.headers=e.headers||{},e.data=o.call(e,e.data,e.headers,e.transformRequest),e.headers=r.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),r.forEach(["delete","get","head","post","put","patch","common"],(function(t){delete e.headers[t]})),(e.adapter||s.adapter)(e).then((function(t){return u(e),t.data=o.call(e,t.data,t.headers,e.transformResponse),t}),(function(t){return i(t)||(u(e),t&&t.response&&(t.response.data=o.call(e,t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)}))}},function(e,t,n){"use strict";var r=n(0),o=n(1);e.exports=function(e,t,n){var i=this||o;return r.forEach(n,(function(n){e=n.call(i,e,t)})),e}},function(e,t,n){"use strict";var r=n(0);e.exports=function(e,t){r.forEach(e,(function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])}))}},function(e,t,n){"use strict";var r=n(7);e.exports=function(e,t,n){var o=n.config.validateStatus;n.status&&o&&!o(n.status)?t(r("Request failed with status code "+n.status,n.config,null,n.request,n)):e(n)}},function(e,t,n){"use strict";var r=n(0);e.exports=r.isStandardBrowserEnv()?{write:function(e,t,n,o,i,s){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(i)&&a.push("domain="+i),!0===s&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},function(e,t,n){"use strict";var r=n(21),o=n(22);e.exports=function(e,t){return e&&!r(t)?o(e,t):t}},function(e,t,n){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t,n){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(0),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,i,s={};return e?(r.forEach(e.split("\n"),(function(e){if(i=e.indexOf(":"),t=r.trim(e.substr(0,i)).toLowerCase(),n=r.trim(e.substr(i+1)),t){if(s[t]&&o.indexOf(t)>=0)return;s[t]="set-cookie"===t?(s[t]?s[t]:[]).concat([n]):s[t]?s[t]+", "+n:n}})),s):s}},function(e,t,n){"use strict";var r=n(0);e.exports=r.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement("a");function o(e){var r=e;return t&&(n.setAttribute("href",r),r=n.href),n.setAttribute("href",r),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,""):"",host:n.host,search:n.search?n.search.replace(/^\?/,""):"",hash:n.hash?n.hash.replace(/^#/,""):"",hostname:n.hostname,port:n.port,pathname:"/"===n.pathname.charAt(0)?n.pathname:"/"+n.pathname}}return e=o(window.location.href),function(t){var n=r.isString(t)?o(t):t;return n.protocol===e.protocol&&n.host===e.host}}():function(){return!0}},function(e,t,n){"use strict";var r=n(10).version,o={};["object","boolean","number","function","string","symbol"].forEach((function(e,t){o[e]=function(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}}));var i={};o.transitional=function(e,t,n){function o(e,t){return"[Axios v"+r+"] Transitional option '"+e+"'"+t+(n?". "+n:"")}return function(n,r,s){if(!1===e)throw new Error(o(r," has been removed"+(t?" in "+t:"")));return t&&!i[r]&&(i[r]=!0,console.warn(o(r," has been deprecated since v"+t+" and will be removed in the near future"))),!e||e(n,r,s)}},e.exports={assertOptions:function(e,t,n){if("object"!=typeof e)throw new TypeError("options must be an object");for(var r=Object.keys(e),o=r.length;o-- >0;){var i=r[o],s=t[i];if(s){var a=e[i],u=void 0===a||s(a,i,e);if(!0!==u)throw new TypeError("option "+i+" must be "+u)}else if(!0!==n)throw Error("Unknown option "+i)}},validators:o}},function(e,t,n){"use strict";var r=n(2);function o(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var n=this;this.promise.then((function(e){if(n._listeners){var t,r=n._listeners.length;for(t=0;t<r;t++)n._listeners[t](e);n._listeners=null}})),this.promise.then=function(e){var t,r=new Promise((function(e){n.subscribe(e),t=e})).then(e);return r.cancel=function(){n.unsubscribe(t)},r},e((function(e){n.reason||(n.reason=new r(e),t(n.reason))}))}o.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},o.prototype.subscribe=function(e){this.reason?e(this.reason):this._listeners?this._listeners.push(e):this._listeners=[e]},o.prototype.unsubscribe=function(e){if(this._listeners){var t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1)}},o.source=function(){var e;return{token:new o((function(t){e=t})),cancel:e}},e.exports=o},function(e,t,n){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},function(e,t,n){"use strict";e.exports=function(e){return"object"==typeof e&&!0===e.isAxiosError}}])}));
//# sourceMappingURL=axios.min.map

3.2vue.js(太长了,省略)

4.启动工程,访问ajax.html

4.1ajax技术不能处理后端返回的重定向结果

第一次请求调完给发回来一个重定向,又去调用一次

第二次请求返回了一个页面,但是浏览器没有跳转,这里的文本框展示调用接口的返回值

5.配置security返回json数据

5.1未登录时访问受限资源的处理

默认情况会返回一个重定向到登录页面;需要调整成返回json数据

5.2自定义一个AuthenticationEntryPoint接口的实现类,在config包下新建类为NoLoginHandle

package com.hhh.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

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 NoLoginHandle  implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        //写处理逻辑,后端给前端返回json数据
        //构造json数据
        Map<String,String> map=new HashMap<>();
        map.put("code","401");
        map.put("message","未登录");

        //转出为json数据
        ObjectMapper mapper=new ObjectMapper();
        String json = mapper.writeValueAsString(map);

        //设置一个响应头ContentType
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        //将数据放入response里
        httpServletResponse.getWriter().print(json);



    }
}

5.3在SecurityConfig类中配置异常处理器

//配置异常处理器,增加
        http.exceptionHandling().authenticationEntryPoint(new NoLoginHandle());

5.4启动工程,访问ajaxTest.html,点击请求res1

 5.5点击确定后,再次点击请求res1,点击F12 ,点击res1,点击Response

 5.6前端接收json数据进行判断

刚刚调用res1接口,返回的是一个重定向,又发起一个login页面的请求,现在直接返回一个已请求的res1,然后返回一个未登录的数据

这时前端会接收到这样一个json数据值就进行判断

6.使用内存配置用户直接访问登录页面

这里在SecurityConfi该类中的内存配置用户的注释放开,将MyUserDetailsService类中配置注释

 6.1这时再去ajaxTest页面中请求res1就成功

在login.html登录成功后,cookie保存在浏览器中,又因为两者url在同一个域下(域名一样),再用ajax去发起请求,ajax会带上同源的cookie

 6.2如果点击请求res2,会受到权限控制

内存配置用户设置了p1权限,但是在SecurityConfig类中的configure方法下res2需要p2权限才可以访问

 

6.自定义一个AccessDeniedHandler接口的实现类,在config包下新建类为NoAuthHandler

package com.hhh.config;

import com.fasterxml.jackson.databind.ObjectMapper;
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.util.HashMap;
import java.util.Map;

public class NoAuthHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        //写处理逻辑,后端给前端返回json数据
        //构造json数据
        Map<String,String> map=new HashMap<>();
        map.put("code","403");
        map.put("message","无权限");

        //转出为json数据
        ObjectMapper mapper=new ObjectMapper();
        String json = mapper.writeValueAsString(map);

        //设置一个响应头ContentType
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        //将数据放入response里
        httpServletResponse.getWriter().print(json);
    }
}

6.4在SecurityConfig类中配置res2没有权限登录后返回json数据

 //没有权限就会被accessDeniedHandler()拦截,需要自定义AccessDeniedHandler接口的实现类
        http.exceptionHandling()
                .authenticationEntryPoint(new NoLoginHandle())
                .accessDeniedHandler(new NoAuthHandler());

6.5启动工程,访问res1,res2查看效果

访问ajaxTest.html时均是未登录

 访问login.html,登录成功后再次访问res1和res2,res1请求成功,res2无权限

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值