Spring Boot | Spring Boot 中 自定义“用户退出控制“、获取“登录用户信息“

news2025/1/25 9:07:22

目录:

    • 一、SpringBoot 中 自定义 "用户授权管理" ( 总体内容介绍 ) :
    • 二、 自定义 "用户退出控制" ( 通过 "HttpSecurity类" 的 logout( )方法来实现 "自定义用户用户登录控制" ) :
      • 1.基础项目文件准备
      • 2.实现 "自定义身份认证" ( UserDetailsService身份认证 )
        • ① service层中类 获取 "用户基本信息" 和 "用户权限信息"
        • ② "自定义类" 实现 "UserDetailsService接口" , 在该类中 封装 "用户身份认证信息"
        • ③ "SecurityConfig配置类" 中 实现 "自定义 身份认证"
      • 3.实现 "自定义用户访问"
        • ④ "SecurityConfig配置类" 中 实现 "自定义 用户访问控制"
        • ⑤ controller层中 实现 "路径访问跳转"
      • 4.实现 "自定义用户登录"
        • ⑥ 自定义 用户登录 "页面"
        • ⑦ 自定义 用户登录 "跳转"
        • ⑧ 自定义 用户登录 "控制"
      • 5.实现 "自定义用户退出"
        • ⑨ 添加自定义 "用户退出链接"
        • ⑩ 添加自定义 "用户退出控制"
        • ⑪ 效果测试
    • 三 、获取 "登录用户信息
      • 3.1 使用 SecurityContextHolder 获取用户信息 ( 建议使用该方式 )
      • 3.2 使用 HttpSession"获取用户信息"

在这里插入图片描述

作者简介 :一只大皮卡丘,计算机专业学生,正在努力学习、努力敲代码中! 让我们一起继续努力学习!

该文章参考学习教材为:
《Spring Boot企业级开发教程》 黑马程序员 / 编著
文章以课本知识点 + 代码为主线,结合自己看书学习过程中的理解和感悟 ,最终成就了该文章

文章用于本人学习使用 , 同时希望能帮助大家。
欢迎大家点赞👍 收藏⭐ 关注💖哦!!!

(侵权可联系我,进行删除,如果雷同,纯属巧合)


一、SpringBoot 中 自定义 “用户授权管理” ( 总体内容介绍 ) :

  • 一个系统建立之后,通常需要适当地做一些权限控制,使得 不同用户具有不同的权限 操作系统
    例如一般的项目中都会做一些简单的登录控制,只有特定用户才能登录访问。接下来将 针对 Web 应用中常见自定义用户授权管理 进行 介绍

  • SpringBoot自定义 “用户授权管理”实现方式 :
    创建类 继承(extens) WebSecurityConfigurerAdapter类

    重写 WebSecurityConfigurerAdapter类 中的 configure( HttpSecurity http )方法

    通过 HttpSecurity类 中的 Xxx方法 来 实现 自定义 “用户授权管理”

    ( 通过 configure( HttpSecurity http ) 方法 中的 HttpSecurity 类 实现/进行 “用户授权管理” )

  • HttpSecurity类主要方法说明 ( 通过 该类中的方法 来实现 "用户授权管理"):

    方法描述
    authorizeRequests( ) :
    授权请求
    开启基于 “HttpServletRequest” 请求访问限制
    ps :
    用于实现 “自定义用户访问控制
    ( 通过configure( HttpSecurity http)方法 中的 HttpSecurity类authorizeRequests( )方法实现 “自定义用户访问控制”其他方法则是以此类推。)
    formLogin( )开启基于表单用户登录
    ps :
    用于实现 “自定义用户登录页面
    使用该方法就是 使用 security提供的"默认登录"页面进行"登录验证" ( 如果没有指定 "自定义的登录页面" 的话 )
    httpBasic( )开启基于 HTTP 请求Basic 认证登录
    logout( )开启退出登录支持
    sessionManagement( )开启 Session 管理配置
    rememberMe( )开启 记住我 功能
    csrf( )配置 “CSRF” 跨站请求伪造防护功能

    补充 :
    configure ( HttpSecurity http )方法参数类型HttpSecurity 类HttpSecurity 类提供了 Http请求的限制权限Session 管理配置CSRF跨站请求问题等方法

二、 自定义 “用户退出控制” ( 通过 “HttpSecurity类” 的 logout( )方法来实现 “自定义用户用户登录控制” ) :

  • 自定义 "用户退出控制"主要考虑退出后会话如何管理以及跳转到哪个页面HtpSecurity 类的 logout( )方法用来处理用户退出
    用户退出控制默认处理路径为 “/ogout”的 Post类型请求,同时也会 清除 SessionRemember Me (记住我) 等任何默认用户配置

  • 自定义 “用户登录控制”具体实际操作为 : 通过 WebSecurityConfigurerAdapter 类configure( HttpSecurity http )方法 中的 HttpSecurity 类中的 logout( )方法可以实现 "自定义用户访问控制" 。

  • logout( )方法 中 的 主要方法说明下表所示 :

    方法 ( 实现 “用户退出控制” 的效果的 方法 )描述
    logoutUrl( String logoutUrl )方法指定 用户退出处理控制 URL默认post 请求的 /logout ( 如不修改默认路径前端进行 "用户退出"时就要 通过访问 “该路径” 来进行 “用户退出”) 。
    ps :
    比如 设置了 .logoutUrl(“/mylogout”) , 前端页面中就要访问 /mylogout 这个路径来进行 “用户退出” 操作 ,通过 该路径方法类型 要为 : post
    logoutSuccessUrl( String logoutSuccessUrl )方法指定 用户退出成功后重定向地址
    ps :
    用户退出成功后就 "跳转的地址"。
    logoutSuccessHandler( LogoutSuccessHandler logoutSuccessHandler )方法指定 用户退出成功后的处理器设置
    deleteCookies( String … cookieNamesToClear )方法用户退出后删除指定 Cookie
    invalidateHttpSession( boolean invalidateHttpSession )方法用户退出后 “是否立即” 清除 Session ( 默认true )。
    clearAuthentication( boolean clearAuthentication )方法用户退出后 “是否立即” 清除 Authentication 用户认证信息 ( 默认true )。

1.基础项目文件准备

  • 创建项目 :

    在这里插入图片描述

  • 项目结构 :

    在这里插入图片描述

  • pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.3.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.itheima</groupId>
        <artifactId>chapter07</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>chapter07</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <!-- Security与Thymeleaf整合实现前端页面安全访问控制 -->
            <dependency>
                <groupId>org.thymeleaf.extras</groupId>
                <artifactId>thymeleaf-extras-springsecurity5</artifactId>
            </dependency>
    
            <!-- JDBC数据库连接启动器 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
    
            <!-- MySQL数据连接驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
    
            <!-- Redis缓存启动器-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
            <!-- Spring Data JPA操作数据库  -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
    
                  <!-- Spring Security提供的安全管理依赖启动器 -->
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-security</artifactId>
          </dependency>
    
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-thymeleaf</artifactId>
          </dependency>
    
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-web</artifactId>
          </dependency>
    
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-test</artifactId>
              <scope>test</scope>
          </dependency>
      </dependencies>
    
          <!--    <build>-->
      <!--        <plugins>-->
      <!--            <plugin>-->
      <!--                <groupId>org.springframework.boot</groupId>-->
      <!--                <artifactId>spring-boot-maven-plugin</artifactId>-->
      <!--            </plugin>-->
      <!--        </plugins>-->
      <!--    </build>-->
          </project>
    
    • 导入 Sql文件 ( 创建数据库表 ) :
      security.sql

    • 创建实体类 :

      Customer.java :

      import javax.persistence.*;
        @Entity(name = "t_customer")
        public class Customer {
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY) //主键自增
            private Integer id;
            private String username;
            private String password;
              public Integer getId() {
            return id;
        }
      
        public void setId(Integer id) {
            this.id = id;
        }
      
        public String getUsername() {
            return username;
        }
      
        public void setUsername(String username) {
            this.username = username;
        }
      
        public String getPassword() {
            return password;
        }
      
        public void setPassword(String password) {
            this.password = password;
        }
      
        @Override
        public String toString() {
            return "Customer{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", password=" + password +
                         '}';
              }
      }
      

    Authority.java

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    
    @Entity(name = "t_authority ")
    public class Authority {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY) //主键自增
        private Integer id;
        private String authority ;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getAuthority() {
            return authority;
        }
    
        public void setAuthority(String authority) {
            this.authority = authority;
        }
    
        @Override
        public String toString() {
            return "Authority{" +
                    "id=" + id +
                    ", authority='" + authority + '\'' +
                    '}';
        }
    }
    
  • 创建Repository接口文件 : ( 通过该接口的方法操作数据 ) :

    CustomerRepository.java :

    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface CustomerRepository extends JpaRepository<Customer,Integer> {
        //根据username查询Customer对象信息
        Customer findByUsername(String username);
    }
    

    AuthorityRepository.java ( 接口文件 ) :

    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Query;
    
    import java.util.List;
    
    public interface AuthorityRepository extends JpaRepository<Authority,Integer> {
    
        //根据 username 来查询"权限信息"
        @Query(value = "select a.* from t_customer c,t_authority a,t_customer_authority ca where ca.customer_id=c.id and ca.authority_id=a.id and c.username =?1",nativeQuery = true)
        public List<Authority> findAuthoritiesByUsername(String username);
    
    }
    
  • 自定义序列化机制 :

    RedisConfig.java :

    package com.itheima.config;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheConfiguration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import java.time.Duration;
    
    @Configuration
    public class RedisConfig { //在该配置类中 自定义存储到Redis数据库的数据的 "序列化机制"
        /**
         *  定制Redis API模板RedisTemplate
         * @param redisConnectionFactory
         * @return
         */
        @Bean
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<Object, Object> template = new RedisTemplate();
            template.setConnectionFactory(redisConnectionFactory);
            // 使用JSON格式序列化对象,对缓存数据key和value进行转换
            Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
            // 解决查询缓存转换异常的问题
            ObjectMapper om = new ObjectMapper();
            // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jacksonSeial.setObjectMapper(om);
            // 设置RedisTemplate模板API的序列化方式为JSON
            template.setDefaultSerializer(jacksonSeial);
            return template;
        }
    }
    
  • application.properties :

    spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC
    spring.datasource.username=root
    spring.datasource.password=root
    
    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    spring.redis.password=123456
    
    spring.thymeleaf.cache=false
    
  • 创建 html资源文件 :
    index.html 页面是 项目首页页面commonvip文件夹中分别对应普通用户VIP用户可访问的页面

    index.html

    <!DOCTYPE html>
    <!-- 配置开启thymeleaf模板引擎页面配置 -->
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>影视直播厅</title>
    </head>
    <body>
    
    <!-- index.html页面是项目首页页面,common和vip文件夹中分别对应的普通用户 和 VIP用户可访问的页面 -->
    <h1 align="center">欢迎进入电影网站首页</h1>
    <hr>
    <h3>普通电影</h3>
    <ul>
        <li><a th:href="@{/detail/common/1}">飞驰人生</a></li>
        <li><a th:href="@{/detail/common/2}">夏洛特烦恼</a></li>
    </ul>
    <h3>VIP专享</h3>
    <ul>
        <li><a th:href="@{/detail/vip/1}">速度与激情</a></li>
        <li><a th:href="@{/detail/vip/2}">猩球崛起</a></li>
    </ul>
    </body>
    </html>
    

    1.html : ( 其他三个页面 以此类推 )

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <!-- th:href="@{/}" : 返回项目首页-->
    <a th:href="@{/}">返回</a>
    <h1>飞驰人生</h1>
    .....
    </body>
    </html>
    

2.实现 “自定义身份认证” ( UserDetailsService身份认证 )

① service层中类 获取 “用户基本信息” 和 “用户权限信息”
  • service层 类中来从 Redis获取 “缓存数据”,如没找到缓存数据,则从Mysql数据库查询数据 : ( 获取 ① “用户基本信息”② “用户权限信息” )

    CustomerService.java :

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    /**
     * 该Service层类中实现的代码效果为:
     * 在Reids数据库中查找是否有指定"缓存数据",有则从其中获取,没有则在Mysql数据库中查找,同时还将查找到的数据也在Redis中进行"缓存"
     */
    @Service //加入到IOC容器中
    public class CustomerService {
        @Autowired
        private CustomerRepository customerRepository;
        @Autowired
        private AuthorityRepository authorityRepository;
    
        @Autowired
        private RedisTemplate redisTemplate;  //通过 Redis API 的方式来进行 "Redis缓存"
    
        /**
         * 业务控制 : 使用唯一用户名查询用户信息
         */
        public Customer getCustomer(String username){
            Customer customer=null;
            //从Redis数据库中获取"缓存数据"
            Object o = redisTemplate.opsForValue().get("customer_"+username);
            //判断是否有该缓存数据
            if(o!=null){
                customer=(Customer)o;
            }else { //不存在该缓存数据,则从数据库中查询缓存数据
                customer = customerRepository.findByUsername(username); //根据username来在数据库中查询数据
                if(customer!=null){
                    redisTemplate.opsForValue().set("customer_"+username,customer);
                }
            }
            return customer;
        }
    
        /**
         * 业务控制 : 使用"唯一用户名"查询用户权限
         */
        public List<Authority> getCustomerAuthority(String username){
            List<Authority> authorities=null;
            //尝试从Redis数据库中获得缓存数据
            Object o = redisTemplate.opsForValue().get("authorities_"+username);
            if(o!=null){
                authorities=(List<Authority>)o;
            }else {
                //没找到缓存数据则从Mysql数据库中查询数据
                authorities=authorityRepository.findAuthoritiesByUsername(username);
                if(authorities.size()>0){
                    redisTemplate.opsForValue().set("authorities_"+username,authorities);
                }
            }
            return authorities;
        }
    }
    
② “自定义类” 实现 “UserDetailsService接口” , 在该类中 封装 “用户身份认证信息”
  • 自定义一个 , 该 实现UserDetailsService接口 , 用该接口loadUserByUsername( )方法 来封装 "用户认证信息" , 该最终被用于 configure ( AuthenticationManagerBuilder auth ) 方法中,被最终用于 "UserDetailsService"身份认证

    UserDetailsServiceImpl.java

    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.*;
    import org.springframework.stereotype.Service;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.Collectors;
    
    /**
     *   自定义一个类, 该类实现了UserDetailsService接口 , 用该接口的 loadUserByUsername()方法来封装"用户认证信息" ,
     *   该类最终被用于 configure(AuthenticationManagerBuilder auth)方法中,被最终用于 "UserDetailsService"身份认证。
     */
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService { //实现 UserDetailsService接口
        @Autowired
        private CustomerService customerService;
    
        @Override
        public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //辅助进行"用户身份认证"的方法
    
            //通过业务方法(业务层类)获取用户以及权限信息
            //根据username获得Customer对象信息
            Customer customer = customerService.getCustomer(s);
            List<Authority> authorities = customerService.getCustomerAuthority(s);
    
            /**
             *  .stream() : 将"权限信息"集合 转换为一个流(Stream) , 以便进行后续的流式操作
             *
             *  .map(authority -> new SimpleGrantedAuthority(authority.getAuthority())) :
             *     使用map操作 / map()函数 来转换流中的每个元素 (将流中的"每一个元素"转换为 "另一种形式")
             *     具体分析:
             *     authority -> 获得流中的每一个元素,将其转换为另一种形式
             *     authority.getAuthority() : 获得 authority 这个元素对象的"权限信息" ( 是一个"权限信息"的字符串 )
             *     new SimpleGrantedAuthority(authority.getAuthority())) : 使用这个 "权限信息字符串" 创建一个 SimpleGrantedAuthority对象,
             *     该对象 是 Spring Security框架中用于表示 "授权信息" 的类
             *
             *   .collect(Collectors.toList()); : 是一个终端操作,它告诉流如何收集其元素以生成一个结果。
             *     具体分析:
             *     .collect(Collectors.toList()) : 收集转换后的 SimpleGrantedAuthority对象,并将其放入一个新的列表中
             */
            // 对"用户权限信息"进行封装
            List<SimpleGrantedAuthority> list = authorities.stream().map(authority -> new SimpleGrantedAuthority(authority.getAuthority())).collect(Collectors.toList());
    
            /*
              创建 UserDetails (用户详情) 对象,并将该对象进行返回
             */
            if(customer!=null){
                //用 username 、password 、权限信息集合 作为参数创建 UserDetails对象 ( 用户详情对象 )
                UserDetails userDetails= new User(customer.getUsername(),customer.getPassword(),list);
                return userDetails;
            } else {
                //如果查询的用户不存在 (用户名不存在 ) , 必须抛出异常
                throw new UsernameNotFoundException("当前用户不存在!");
            }
        }
    }
    
③ “SecurityConfig配置类” 中 实现 “自定义 身份认证”
  • SecurityConfig配置类” 中 实现 “自定义身份认证 :

    SecurityConfig.java

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    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.crypto.bcrypt.BCryptPasswordEncoder;
    
    @EnableWebSecurity  // 开启MVC security安全支持
    public class SecurityConfig extends WebSecurityConfigurerAdapter { //在该配置类中 配置①自定义用户认证(UserDetailsService) 和 ②用户授权管理自定义配置(自定义用户访问控制)
    
        /*
         该类为 配置好了 "UserDetailsService"身份认证信息 的类 ,
         使用该类来在 configure(AuthenticationManagerBuilder auth)方法中进行 "UserDetailsService"身份认证。
         */
        @Autowired
        private UserDetailsServiceImpl userDetailsService;
          /**
       * 用户身份认证自定义配置 ( 通过UserDetailsService的方式实现 )
       *
       * 重写configure(AuthenticationManagerBuilder auth)方法 : 自定义用户认证
       */
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          //密码需要设置编码器 ( 添加"密码编辑器")
          BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
          //使用UserDetailService进行"身份认证"
          auth.userDetailsService(userDetailsService)
                  //设置"密码编辑器"
                  .passwordEncoder(encoder);
         }
    }    
    

3.实现 “自定义用户访问”

④ “SecurityConfig配置类” 中 实现 “自定义 用户访问控制”
  • SecurityConfig配置类” 中 实现 自定义 “用户访问控制” :

    SecurityConfig.java

    package com.itheima.config;
    
    import com.itheima.service.Impl.UserDetailsServiceImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    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.crypto.bcrypt.BCryptPasswordEncoder;
    
    @EnableWebSecurity  // 开启MVC security安全支持
    public class SecurityConfig extends WebSecurityConfigurerAdapter { //在该配置类中 配置①自定义用户认证(UserDetailsService) 和 ②用户授权管理自定义配置(自定义用户访问控制)
    
        /**
         * 用户授权管理自定义配置 ( 自定义用户访问控制 )
         *
         * 重写configure(HttpSecurity http)方法 : 自定义用户访问控制
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            /**
             *  .antMatchers : 开启Ant风格的路径匹配
             *  .permitAll() : 无条件对请求进行放行
             *  .antMatchers("/").permitAll() : //对/请求进行放行
             *
             *  .hasRole() : 匹配用户是否是"某一个角色"
             *  .hasAnyRole() : 匹配用户是否是"某一个角色" 或 是 "另一个角色" ( 匹配"用户"是否有参数中的"任意角色" )
             *  .antMatchers("detail/common/**").hasAnyRole("common","vip") :  表示 detail/common/** 请求只有用户角色common或用户vip才允许访问/才能访问
             *    ---( 即ROLE_common权限 和 (即ROLE_vip权限才能访问该路径 )
             *  .antMatchers("/detail/vip/**").hasRole("vip") :  表示 detail/vip/** 请求只有用户vip才允许访问/才能访问
             *
             *  .anyRequest() :    匹配任何请求
             *  .authenticated() : 匹配已经登陆认证的用户
             *  .and() //功能连接符
             *  .formLogin() : 启用Spring Security的基于"HTML表单"的 用户登录功能/用户登录页面, 同时通过该页面来进行"登录验证"
             *    ---简而言之 : 使用security提供的"默认登录"页面进行"登录验证" (如果没有指定"自定义的登录页面"的话)
             *
             */
            // 自定义用户访问控制 
            http.authorizeRequests()
                    .antMatchers("/").permitAll()  //对/请求进行放行
                    .antMatchers("/detail/common/**").hasAnyRole("common","vip") //普通电影,则是common和vip用户都能看
                    .antMatchers("/detail/vip/**").hasRole("vip")  //vip电影则是vip用户才能看
                    .anyRequest()  //匹配任何请求
                    .authenticated()  //匹配已经登陆认证的用户
                    .and() //功能连接符
                    .formLogin(); //使用security提供的"默认登录"页面进行"登录验证" (如果没有指定"自定义的登录页面"的话)
        }
    
    }
    
⑤ controller层中 实现 “路径访问跳转”
  • controller层中 实现 "路径访问跳转" :

    LoginController.java

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller //加入IOC容器中
    public class LoginController {  //关于跳转到登录页面有关的controller类
    
         /*
           跳转到Detail文件夹下的"视图页面"
         */
        @GetMapping("/detail/{type}/{path}") //其中的变量为"路径变量"
        // @PathVariable注解获得"路径变量"的值
        public String toDetail( @PathVariable("type") String type ,@PathVariable("path") String path ) {
            //返回值为String,可用于返回一个视图页面
            return "/detail/"+type+"/"+path;
        }
    
    }
    

4.实现 “自定义用户登录”

⑥ 自定义 用户登录 “页面”
  • 要实现 自定义用户登录功能,首先必须根据需要自定义一个用户登录页面。在项目的 resources/templates 目录下新创建一个名为 login文件夹( 专门处理用户登录 ),在该文件夹中创建一个 用户登录页面 : login.html

    login.html

    <!DOCTYPE html>
    <!-- 配置开启thymeleaf"模板引擎"页面配置 -->
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>用户登录界面</title>
        <link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet">
        <link th:href="@{/login/css/signin.css}" rel="stylesheet">
    </head>
    <body class="text-center">
    <!--  form表单请求跳转路径为 : /userLogin  -->
    <form class="form-signin" th:action="@{/userLogin}" th:method="post" >
        <!--  img标签-->
        <img class="mb-4" th:src="@{/login/img/login.jpg}" width="72px" height="72px">
        <h1 class="h3 mb-3 font-weight-normal">请登录</h1>
    
        <!-- 用户登录错误信息提示框 -->
        <!--  th:if 根据条件的真假决定是否渲染 HTML 元素 ,真则渲染,假则不渲染 (有该参数值则渲染,没有该参数值则不渲染)  -->
        <!--  有该参数值则渲染该div,否则就不渲染该div   -->
        <div th:if="${param.error}"
             style="color: red;height: 40px;text-align: left;font-size: 1.1em">
            <!--  img标签  -->
            <img th:src="@{/login/img/loginError.jpg}" width="20px">用户名或密码错误,请重新登录!
        </div>
        <!--  用户名参数名为 : name  , 密码的参数名为: pws  -->
        <input type="text" name="name" class="form-control"
               placeholder="用户名" required="" autofocus="">
        <input type="password" name="pwd" class="form-control"
               placeholder="密码" required="">
        <button class="btn btn-lg btn-primary btn-block" type="submit" >登录</button>
        <p class="mt-5 mb-3 text-muted">Copyright© 2024-2025</p>
    </form>
    </body>
    </html>
    

    上面代码中引入了两个 CSS 样式文件和两个 img图片文件用来渲染用户登录页面,它们都存在于 /ogin/** 目录下,需要提前引入这些静态资源文件
    目录中。引入这些静态资源文件后结构如下图所示 :

    获得相应的css文件 和 图片文件

    在这里插入图片描述

⑦ 自定义 用户登录 “跳转”
  • 自定义 “用户登录跳转” :

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller //加入IOC容器中
    public class LoginController {  //关于跳转到登录页面有关的controller类
    
        /**
         * 跳转到自定义的 "登录页面"
         */
        @GetMapping("/userLogin")
        public String toLoginPage() {
            return "/login/login";
        }
    }
    

    Spring Security 默认采用 Get 方式“/ogin”请求 用于向 “登录页面” 跳转 ( 可自定义跳转"登录页面"的路径,来指定”登录页面“ ),使用 Post 方式的 “/ogin”请求 用于 对登录后的数据处理

⑧ 自定义 用户登录 “控制”
  • 完成上面的准备工作后,打开 SecurityConfig 类重写 configure( HttpSecurity http )方法,实现 用户登录控制
    示例代码如下 :

    SecurityConfig.java

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    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.crypto.bcrypt.BCryptPasswordEncoder;
    
    @EnableWebSecurity  // 开启MVC security安全支持
    public class SecurityConfig extends WebSecurityConfigurerAdapter { //在该配置类中 配置①自定义用户认证(UserDetailsService) 和 ②用户授权管理自定义配置(自定义用户访问控制)
    
        /*
         该类为 配置好了 "UserDetailsService"身份认证信息 的类 ,
         使用该类来在 configure(AuthenticationManagerBuilder auth)方法中进行 "UserDetailsService"身份认证。
         */
        @Autowired
        private UserDetailsServiceImpl userDetailsService;
    
          /**
       * 用户身份认证自定义配置 ( 通过UserDetailsService的方式实现 )
       *
       * 重写configure(AuthenticationManagerBuilder auth)方法 : 自定义用户认证
       */
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          //密码需要设置编码器 ( 添加"密码编辑器")
          BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
          //使用UserDetailService进行"身份认证"
          auth.userDetailsService(userDetailsService)
                  //设置"密码编辑器"
                  .passwordEncoder(encoder);
      }
        
            /**
     * 用户授权管理自定义配置 ( 自定义用户访问控制 )
     *
     * 重写configure(HttpSecurity http)方法 : 自定义用户访问控制 ( 权限管理 )
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         *  .antMatchers : 开启Ant风格的路径匹配
         *  .permitAll() : 无条件对请求进行放行
         *  .antMatchers("/").permitAll() : //对/请求进行放行
         *
         *  .hasRole() : 匹配用户是否是"某一个角色"
         *  .hasAnyRole() : 匹配用户是否是"某一个角色" 或 是 "另一个角色" ( 匹配"用户"是否有参数中的"任意角色" )
         *  .antMatchers("detail/common/**").hasAnyRole("common","vip") :  表示 detail/common/** 请求只有用户角色common或用户vip才允许访问/才能访问
         *    ---( 即ROLE_common权限 和 (即ROLE_vip权限才能访问该路径 )
         *  .antMatchers("/detail/vip/**").hasRole("vip") :  表示 detail/vip/** 请求只有用户vip才允许访问/才能访问
         *
         *  .anyRequest() :    匹配任何请求
         *  .authenticated() : 匹配已经登陆认证的用户
         *  .and() //功能连接符
         *  .formLogin() : 启用Spring Security的基于"HTML表单"的 用户登录功能/用户登录页面, 同时通过该页面来进行"登录验证"
         *    ---简而言之 : 使用security提供的"默认登录"页面进行"登录验证" (如果没有指定"自定义的登录页面"的话)
         *
         */
        // 自定义用户授权管理 (自定义用户访问控制 )
        http.authorizeRequests() 
                .antMatchers("/").permitAll()  //对 "/"请求 进行放行 (进入到项目首页)
                //对static文件夹下的静态资源进行统一放行 (如果没有对静态资源放行,未登录的用户访问项目首页时就无法加载页面关联的静态资源文件 )
                .antMatchers("/login/**").permitAll()
                .antMatchers("/detail/common/**").hasAnyRole("common","vip") //普通电影,则是common和vip用户都能看
                .antMatchers("/detail/vip/**").hasRole("vip")  //vip电影则是vip用户才能看
                .anyRequest()  //匹配任何请求
                .authenticated()  //匹配已经登陆认证的用户
                .and() //功能连接符
                .formLogin(); //使用security提供的"默认登录"页面进行"登录验证" (如果没有指定"自定义的登录页面"的话)
    
       /**
         * 自定义"用户登录控制" :
         *   .loginPage() : 自定义登录页面的"跳转路径" , 跳转到自己定制的"登录页面"
         *   .permitAll() : 无条件对请求进行放行
         *   .usernameParameter("name").passwordParameter("pwd") : 设置登录用户的"用户名参数" 和 "密码参数" (接受登录时提交的"用户名"和"密码")
         *   .defaultSuccessUrl() : 指定用户登录成功后的默认跳转地址
         *   .failureUrl() : 指定用户登录失败后的"跳转地址",默认跳转到 /login?error
         *     ---error时一个错误标识,作用是在登录失败后在登录页面进行接收判断,例如login.html示例中的${param.error},这两者必须保持一直。
         */
        //自定义"用户登录控制"
        http.formLogin()
                .loginPage("/userLogin").permitAll() //自定义登录页面的"跳转路径"
                .usernameParameter("name").passwordParameter("pwd")  //设置登录用户的"用户名参数" 和 "密码参数"
                .defaultSuccessUrl("/") //用户登录成功后默认跳转到"项目首页"
                .failureUrl("/userLogin?error"); //用户登录失败后默认跳转到"项目首页"
      }
    }    
    

5.实现 “自定义用户退出”

⑨ 添加自定义 “用户退出链接”
  • 要实现 自定义用户退出功能,必须某个页面定义用户退出链接或者 按钮。为了简化操作我们在之前创建的项目首页 : index.html 上方新增一个用户退出链接修改后示范文件代码如下所示 :

    <!-- th:action : 用于指定“表单提交”时 “应发送的URL”-->
    <!-- 跳转到 /mylogout路径来进行 "用户退出" , 因为不是使用默认的 /logout路径来请求 "用户退出"  所以 后端的 .logoutUrl()方法中内容也修改为 /mylogout -->
    <!-- 同时 /mylogout 请求的方法为post类型的-->
    <form th:action="/mylogout" method="post">
        <input th:type="submit" th:value="注销">
    </form>
    

    index.html

    <!DOCTYPE html>
    <!-- 配置开启thymeleaf模板引擎页面配置 -->
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
    	<meta charset="UTF-8">
    	<title>影视直播厅</title>
    </head>
    <body>
    
    <!-- index.html页面是项目首页页面,common和vip文件夹中分别对应的普通用户 和 VIP用户可访问的页面 -->
    <h1 align="center">欢迎进入电影网站首页</h1>
    <!-- th:action : 用于指定“表单提交”时 “应发送的URL”-->
    <!-- 跳转到 /mylogout路径来进行 "用户退出" , 因为不是使用默认的 /logout路径来请求 "用户退出"  所以 后端的 .logoutUrl()方法中内容也修改为 /mylogout -->
    <!-- 同时 /mylogout 请求的方法为post类型的-->
    <form th:action="/mylogout" method="post">
        <input th:type="submit" th:value="注销">
    </form>
    <hr>
    <h3>普通电影</h3>
    <ul>
    	<li><a th:href="@{/detail/common/1}">飞驰人生</a></li>
    	<li><a th:href="@{/detail/common/2}">夏洛特烦恼</a></li>
    </ul>
    <h3>VIP专享</h3>
    <ul>
    	<li><a th:href="@{/detail/vip/1}">速度与激情</a></li>
    	<li><a th:href="@{/detail/vip/2}">猩球崛起</a></li>
    </ul>
    </body>
    </html>
    

    上面的代码中新增了一个 <form>标签进行注销控制 ( 进行 “用户退出控制”),且定义的退出表单 aciton 为“/mylogout ( 默认为“/logout” ),方法post


    需要说明的是,Spring Boot 项目中引入 Spring Security 框架后会 自动开启 : CSRF 防护功能 ( 跨站请求伪造防护),用户退出必须使用 POST
    ; 如果关闭CSRF 防护功能,那么 可以使用任意方式HTTP 请求进行用户注销

⑩ 添加自定义 “用户退出控制”
  • SecurityConfig.java

    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    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.crypto.bcrypt.BCryptPasswordEncoder;
    
    @EnableWebSecurity  // 开启MVC security安全支持
    public class SecurityConfig extends WebSecurityConfigurerAdapter { //在该配置类中 配置①自定义用户认证(UserDetailsService) 和 ②用户授权管理自定义配置(自定义用户访问控制)
    
        /*
         该类为 配置好了 "UserDetailsService"身份认证信息 的类 ,
         使用该类来在 configure(AuthenticationManagerBuilder auth)方法中进行 "UserDetailsService"身份认证。
         */
        @Autowired
        private UserDetailsServiceImpl userDetailsService;
        
          /**
       * 用户身份认证自定义配置 ( 通过UserDetailsService的方式实现 )
       *
       * 重写configure(AuthenticationManagerBuilder auth)方法 : 自定义用户认证
       */
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          //密码需要设置编码器 ( 添加"密码编辑器")
          BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
          //使用UserDetailService进行"身份认证"
          auth.userDetailsService(userDetailsService)
                  //设置"密码编辑器"
                  .passwordEncoder(encoder);
      }
        
        
          /**
       * 用户授权管理自定义配置 ( 自定义用户访问控制 )
       *
       * 重写configure(HttpSecurity http)方法 : 自定义用户访问控制 ( 权限管理 )
       */
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          /**
           *  .antMatchers : 开启Ant风格的路径匹配
           *  .permitAll() : 无条件对请求进行放行
           *  .antMatchers("/").permitAll() : //对/请求进行放行
           *
           *  .hasRole() : 匹配用户是否是"某一个角色"
           *  .hasAnyRole() : 匹配用户是否是"某一个角色" 或 是 "另一个角色" ( 匹配"用户"是否有参数中的"任意角色" )
           *  .antMatchers("detail/common/**").hasAnyRole("common","vip") :  表示 detail/common/** 请求只有用户角色common或用户vip才允许访问/才能访问
           *    ---( 即ROLE_common权限 和 (即ROLE_vip权限才能访问该路径 )
           *  .antMatchers("/detail/vip/**").hasRole("vip") :  表示 detail/vip/** 请求只有用户vip才允许访问/才能访问
           *
           *  .anyRequest() :    匹配任何请求
           *  .authenticated() : 匹配已经登陆认证的用户
           *  .and() //功能连接符
           *  .formLogin() : 启用Spring Security的基于"HTML表单"的 用户登录功能/用户登录页面, 同时通过该页面来进行"登录验证"
           *    ---简而言之 : 使用security提供的"默认登录"页面进行"登录验证" (如果没有指定"自定义的登录页面"的话)
           *
           */
          // 自定义用户授权管理 (自定义用户访问控制 )
          http.authorizeRequests()
                  .antMatchers("/").permitAll()  //对 "/"请求 进行放行 (进入到项目首页)
                  //对static文件夹下的静态资源进行统一放行 (如果没有对静态资源放行,未登录的用户访问项目首页时就无法加载页面关联的静态资源文件 )
                  .antMatchers("/login/**").permitAll()
                  .antMatchers("/detail/common/**").hasAnyRole("common","vip") //普通电影,则是common和vip用户都能看
                  .antMatchers("/detail/vip/**").hasRole("vip")  //vip电影则是vip用户才能看
                  .anyRequest()  //匹配任何请求
                  .authenticated()  //匹配已经登陆认证的用户
                  .and() //功能连接符
                  .formLogin(); //使用security提供的"默认登录"页面进行"登录验证" (如果没有指定"自定义的登录页面"的话)
          
                /**
           * 自定义"用户登录控制" :
           *   .loginPage() : 自定义登录页面的"跳转路径" , 跳转到自己定制的"登录页面"
           *   .permitAll() : 无条件对请求进行放行
           *   .usernameParameter("name").passwordParameter("pwd") : 设置登录用户的"用户名参数" 和 "密码参数" (接受登录时提交的"用户名"和"密码")
           *   .defaultSuccessUrl() : 指定用户登录成功后的默认跳转地址
           *   .failureUrl() : 指定用户登录失败后的"跳转地址",默认跳转到 /login?error
           *     ---error时一个错误标识,作用是在登录失败后在登录页面进行接收判断,例如login.html示例中的${param.error},这两者必须保持一直。
           */
          //自定义"用户登录控制"
          http.formLogin()
                  .loginPage("/userLogin").permitAll() //自定义登录页面的"跳转路径"
                  .usernameParameter("name").passwordParameter("pwd")  //设置登录用户的"用户名参数" 和 "密码参数"
                  .defaultSuccessUrl("/") //用户登录成功后默认跳转到"项目首页"
                  .failureUrl("/userLogin?error"); //用户登录失败后默认跳转到"项目首页"
          
          
                /**
           * 自定义"用户退出控制"
           */
          http.logout()
                  .logoutUrl("/mylogout") //指定用户退出的请求路径 (前端页面进行"用户退出时,要请求该路径,且方法为post")
                  .logoutSuccessUrl("/"); //退出成功后的重定向地址 (退出成功后跳转的地址)
          		 //用户退出后,用户会话信息是默认清除的,次情况下无需手动配置
    
         }
     }
    
⑪ 效果测试
  • 重启项目,项目启动成功后,通过浏览器访问“http://localhost:8080/”会直接进入到项目首页,如下图所示 :

    在这里插入图片描述

    当输入正确的用户、密码成功登陆后,此时可点击 "注销"按钮 来进行 用户注销,注销后会根据自定义设置重定向项目首页,而此时如果再次访问影 片详情又会被拦截到 “用户登录页面”说明自定义用户退出功能正确实现

三 、获取 "登录用户信息

  • 传统项目中 进行 用户登录处理时,通常会查询用户 "是否存在" ( 验证该用户是否存在 ),如果 存在登录成功,同时 当前 用户放在 Session 中

    前面的案例中,使用 整合 Security 进行 用户授权管理并没有显示登录后用户处理情况 ,那么这种情况下 :

    登录后用户存放在哪里呢 ?
    存储用户数据及结构又是 怎样的呢?

  • 下面将通过 HttpSessionSecurityContextHolder两种方式获取登录后用户信息

3.1 使用 SecurityContextHolder 获取用户信息 ( 建议使用该方式 )

  • 为了简化操作先按照 自定义 “用户退出控制” 敲好一个 基本的项目该项目的基础上进行 "获取用户信息" 的 操作,现在按上面的操作创建好一个基本的项目了,在已有 LoginController 控制类 ( 控制层类 )中新增**一个 用于获取当前会话用户信息getUser( )方法

  • Spring Security 针对 拦截登录用户专门提供了一个 SecurityContextHolder 类,来获取Spring Security应用上下文 : SecurityContex进而获取封装用户信息

    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContext;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.context.SecurityContextImpl;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.servlet.http.HttpSession;
    import java.util.Enumeration;
    
    @Controller //加入IOC容器中
    public class LoginController { 
    
        @GetMapping("/getUserByContext")
        @ResponseBody
        public void getUser2() {
            //获取应用上下文
            SecurityContext context = SecurityContextHolder.getContext();
            System.out.println("userDetails:" + context);
            //获取用户相关信息
            Authentication authentication = context.getAuthentication();
            UserDetails principal = (UserDetails)authentication.getPrincipal();
            System.out.println(principal);
            System.out.println("username:"+principal.getUsername());
            System.out.println("password:"+principal.getPassword());
            System.out.println("Authorities:"+principal.getAuthorities());
        }
    
    }
    

    上述代码中,通过 Security 提供的 SecurityContextHolder 类先获取了 应用上下文对象 : SecurityContext,并通过 其相关方法获取了当前登录用户信息

    Security 提供的 SecurityContextHolder 相对简便,也是在 Security 项目相对推荐使用方式

3.2 使用 HttpSession"获取用户信息"

  • 为了简化操作先按照 自定义 “用户退出控制” 敲好一个基本的项目该项目的基础上,进行"获取用户信息" 的操作,现在按上面的操作创建好一个基本的项目了,在已有 LoginController 控制类 ( 控制层类 )中新增一个 用于获取当前会话用户信息getUser( )方法

    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContext;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.context.SecurityContextImpl;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.servlet.http.HttpSession;
    import java.util.Enumeration;
    
    @Controller //加入IOC容器中
    public class LoginController {  
    
        /**
         * 获取当前会话信息 (获取登录用户的信息)
         */
        @GetMapping("/getUserBySession")
        @ResponseBody 
        public void getUser(HttpSession session) {
            //从当前HttpSession中获取绑定到"此会话"的"所有对象"的名称
            Enumeration<String> names = session.getAttributeNames(); //获取绑定到"此会话"的"所有对象"的名称
            while (names.hasMoreElements()) { //判断names所代表的Enumeration中是否还有更多的元素,如果names中还有没遍历的属性名就返回true,否则返回false
                //获取HttpSession中的"会话名称"
                String element = names.nextElement();
                //获取HttpSession中的"应用上下文"
                SecurityContextImpl attribute = (SecurityContextImpl)session.getAttribute(element);
                System.out.println("element:"+element);
                System.out.println("attribute:"+attribute);
    
                //获取用户相关信息
                Authentication authentication = attribute.getAuthentication();
                UserDetails principal = (UserDetails) authentication.getPrincipal();
                System.out.println(principal);
                System.out.println("username:"+principal.getUsername());
            }
        }
    
    }
    

    Security 提供的 HttpSession 方式 比较传统必须引入HttpSession对象操作也更为复杂不推荐使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1681072.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java入门基础学习笔记25——死循环和循环嵌套

死循环&#xff1a; 可以一直执行下去的一种循环&#xff0c;如果没有干预不会停下来。 死循环的写法&#xff1a; 例&#xff1a; package cn.ensource.loop;public class EndLessLoopDemo5 {public static void main(String[] args) {// 目标&#xff1b;掌握死循环的写法w…

GitHub配置SSH协议|什么是SSH

前言 之前用云服务器和GitHub的时候&#xff0c;因为比较懒一直没有配置过ssh。随着最近项目的迫在眉睫&#xff0c;通过Vs Code链接服务器和从GitHub白嫖代码的频率的大幅增加&#xff0c;拒绝接触新事物鼠鼠我只好转求ssh。此篇文章记录了我自己创建ssh密钥、配置ssh密钥的步…

什么是网络端口?为什么会有高危端口?

一、什么是网络端口&#xff1f; 网络技术中的端口默认指的是TCP/IP协议中的服务端口&#xff0c;一共有0-65535个端口&#xff0c;比如我们最常见的端口是80端口默认访问网站的端口就是80&#xff0c;你直接在浏览器打开&#xff0c;会发现浏览器默认把80去掉&#xff0c;就是…

python批量生成防伪识别二维码

欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一.前言 二.代码 三.使用 四.总结 一.前言 二维码(QR Code)是一种矩阵条码技术,它使用黑白矩形图案来表示二进制数据,这些矩形图案可以被设备扫描并解读。二维码可以被用来存储

Spring数据访问全攻略:从JdbcTemplate到声明式事务

上文讲到 —— 航向数据之海&#xff1a;Spring的JPA与Hibernate秘籍 本文目录 四. JdbcTemplate的使用定义JdbcTemplate及其在Spring中的作用展示如何使用JdbcTemplate简化数据库操作1. 配置JdbcTemplate2. 使用JdbcTemplate查询数据3. 打印查询结果 五. Spring的事务管理介绍…

Linux进程(三) --- 状态和优先级

运行&#xff0c;阻塞&#xff0c;挂起 运行 (Running) 当一个进程处于运行状态时&#xff0c;它正在使用CPU执行指令。进程在以下两种情况下可能被认为是运行状态&#xff1a; 实际运行&#xff08;Running on CPU&#xff09;&#xff1a; 进程当前正在CPU上执行。可运行&…

ES6之数值的扩展

1. 数值的扩展 1.1. 二进制和八进制字面量表示:1.2. 数值判断方法: 1.2.1. Number.isFinite() 检查一个值是否为有限的数值。1.2.2. Number.isNaN() 更准确地检测NaN值。1.2.3. 传统的全局方法 isFinite() 和 isNaN() 的区别 1.3. 数值转换方法:1.4. 整数检查与精度: 1.4.1. Nu…

UDP多对多组播通信

广播和多播仅应用于UDP。TCP是一个面向连接的协议&#xff0c;TCP一定是点对点的&#xff0c;一点是两个主机来建立连接的&#xff0c;TCP肯定是单播。只有UDP才会使用广播和组播。 如下示例实现一个UDP多对多的组播通信&#xff0c;进程中有收、发两个线程&#xff0c;分别表…

数字水印 | 奇异值分解 SVD 的定义、原理及性质

目录 1 为什么使用 SVD&#xff1f;2 SVD 的定义是什么&#xff1f;2.1 特征值分解2.2 奇异值分解 3 如何求解奇异值 SV&#xff1f;3.1 求解过程3.2 证明过程 4 什么是 SVD 的性质&#xff1f; 参考博客&#xff1a; Python 机器学习笔记&#xff1a;奇异值分解&…

Java入门基础学习笔记23——For循环结构

1、for循环&#xff1a; 控制一段代码反复执行很多次。 2、For循环语句的基本结构&#xff1a; for(初始化表达式&#xff1b;判断表达式&#xff1b;递增&#xff08;递减&#xff09;表达式&#xff09; {循环体语句&#xff08;重复执行的代码&#xff09; } 例&#xff1…

积温空间分布数据、气温分布数据、日照数据、降雨量分布、太阳辐射数据、地表径流数据、土地利用数据、npp数据、ndvi数据

引言 积温是某一时段内逐日平均气温之和,它是研究植物生长、发育对热量的要求和评价热量资源的一种指标,是影响植物生长的重要因素之一&#xff0c;对指导农业生产和生态建设具有非常重要的意义。作为重要的气候资源&#xff0c;积温与其它资源的区别在于存在很大的地域差异和时…

【再探】设计模式—桥接模式、组合模式及享元模式

结构型设计模式描述了对象与类之间的关系。适配器模式及装饰器模式主要用于接口适配及功能增强&#xff0c;而桥接模式模式则是为了减少类的数量&#xff0c;组合模式让部分与容器能被客户端统一对待处理&#xff0c;享元模式则是用于节约系统内存&#xff0c;提高系统性能。 …

kk聊天室系统源码搭建-自适应手机电脑-秒级响应-群体消息

kk聊天室系统源码搭建-自适应手机电脑-秒级响应-群体消息-单体消息 可以无限创建聊天室&#xff0c;可以把单个聊天室链接拿出来单独使用&#xff0c;消息秒级响应&#xff0c;支持设置屏蔽词。 具体仔细看视频演示&#xff0c;不提供演示&#xff0c;因为青狐资源网会员用户太…

第25次修改留言板,修改了布局,样式和脚本分离

伤心城市 首页 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"beiwanglu" content"widthdevice-width, initial-scale1.0"><link rel"stylesheet" type&qu…

每周题解:牛的旅行

题目描述 牛的旅行 农民John的农场里有很多牧区。有的路径连接一些特定的牧区。一片所有连通的牧区称为一个牧场。但是就目前而言&#xff0c;你能看到至少有两个牧区不连通。 现在&#xff0c;John想在农场里添加一条路径 ( 注意&#xff0c;恰好一条 )。对这条路径有这样的…

宁静致远(“静”)

宁静致远是一个成语&#xff0c;读音为nng jng zh yuǎn&#xff0c;意思是只有心境平稳沉着、专心致志&#xff0c;才能厚积薄发、 有所作为。出自《淮南子:主术训》。 出处 宁静致远张铭篆刻 此句最早出自西汉初年道家刘安的《淮南子:主术训》&#xff0c;蜀汉丞相诸葛亮的…

​学者观察 | 从区块链应用创新看长安链发展——CCF区块链专委会荣誉主任斯雪明

导语 2024年1月27日&#xff0c;斯雪明教授在长安链发布三周年庆暨生态年会上发表演讲&#xff0c;认为在区块链发展过程中&#xff0c;不仅需要技术创新&#xff0c;同时需要有价值、有特色、有示范意义的应用创新。斯雪明教授介绍了国内区块链技术与应用发展的现状、趋势与挑…

C++ I/O流(一)——输出流

一、IO流概念 IO流可分为输入流和输出流,用于从设备(如键盘、文件、网络等)读取数据或向设备写入数据。C++标准库提供了丰富的IO流类,包括iostream、fstream、stringstream等,分别用于处理控制台输入输出、文件输入输出和字符串流操作。 读操作:输入流中读取数据到程序中…

CTFshow 爆破

第一题0 抓包发现输入账号密码后数据包会多一个base64编码 所有推出用户密码在传输的时候进行了加密 所以爆破时也用base64加密 设置paylod 用题目给的字典 在添加两条规则 第一个是增加前缀 admin: 第二个是使得payload进行base64编码 取消URL编码字符 因为会把号编码了 开…

C# .Net8 switch 的用法

在 .net 8中&#xff0c;switch 不需要再和传统的写法一样了&#xff0c;会更加的方便 创建一个 .net 8 控制台项目 switch 的写法没必要和以前一样 namespace SwitchTest {internal class Program{static void Main(string[] args){int day 3;var week day switch{1 > &…