首页 > 其他分享 >Spring Security

Spring Security

时间:2024-12-20 22:28:10浏览次数:9  
标签:用户名 登录 Spring 用户 认证 Security public

一、使用Spring Security 导入依赖,改密码

Spring Security 6.x 的认证实现流程如下:

用户提交登录请求
1. Spring Security 将请求交给 UsernamePasswordAuthenticationFilter 过滤器处理。
2. UsernamePasswordAuthenticationFilter 获取请求中的用户名和密码,并生成一个              AuthenticationToken 对象,将其交给 AuthenticationManager 进行认证。
    AuthenticationManager 通过 UserDetailsService 获取用户信息,然后使用 PasswordEncoder 对用户密码进行校验。

3. 如果密码正确,AuthenticationManager 会生成一个认证通过的 Authentication 对象,并返回给 UsernamePasswordAuthenticationFilter 过滤器。如果密码不正确,则 AuthenticationManager 抛出一个 AuthenticationException 异常。
4. UsernamePasswordAuthenticationFilter 将 Authentication 对象交给 SecurityContextHolder 进行管理,并调用 AuthenticationSuccessHandler 处理认证成功的情况。
5. 如果认证失败,UsernamePasswordAuthenticationFilter 会调用 AuthenticationFailureHandler 处理认证失败的情况。

二、用户自定义登录页面

依赖:

        <!--配置security依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--jsp依赖-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>

在application.yml中配置用户名密码

新建配置类WebSecurityConfig

package com.example.springboottest;

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

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Bean
    public PasswordEncoder passwordEncoder(){
        //Spring Security默认的密码加密器
        return new BCryptPasswordEncoder();
    }

    /**
     * spring security 配置的核心方法
     * @param http 配置的核心对象
     * @throws Exception 配置错误【抛出的异常】
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //HTTP请求认证/鉴权配置信息
        http.authorizeRequests()
                //antMatchers: http请求匹配器,按照指定的patten进行匹配
                //anonymous 匿名访问,只允许匿名状态时候访问,如果已经登录,则无法访问
                .antMatchers("/login.jsp").anonymous()
                //permitAll 赋予所有权限==没有权限的人可以访问
                .antMatchers("/static/**").permitAll()
                //anyRequest() 匹配上方配置外,其他所有请求
                //authenticated() 认证访问,只有认证过的用户才能访问
                .anyRequest().authenticated();

        //登录表单相关的配置
        http.formLogin()
                //登录页面,当用户未登录,访问受保护的资源时,会跳转到该页面
                .loginPage("/login.jsp")
                //登录表单提交地址,即登录表单action地址,即处理登录请求的路径
                .loginProcessingUrl("/login")
                //登录成功后跳转的页面
                .defaultSuccessUrl("/")
                .usernameParameter("username") //表单中提交后用户名的参数名称 登录表单用户名参数名,默认为username
                .passwordParameter("password");//表单中提交后用户名的参数名称 登录表单密码参数名,默认为password
    }
}

然后自己创建login.jsp页面,这样就会使用自己自定义的登录页面。

有个问题,我们不会把用户从配置文件中写死,我们会从数据库中查找用户然后根据数据库匹配登录是否成功。

三、自定义获取用户认证/权限信息

       UserDetails对象

在spring Security中,所有的用户最终认证后都会被封装为userDetails接口的实现类,该接口主要定义如下几个方法:

public interface UserDetails extends Serializable {
    //返回认证用户的所有权限
    Collection<? extends GrantedAuthority> getAuthorities();
    //返回认证用户不的密码
    String getPassword();
    //返回认证用户不的用户名
    String getUsername();
    //账户是否未过期
    boolean isAccountNonExpired();
    //账号是否为解锁状态
    boolean isAccountNonLocked();
    //账号的凭证是否未过期
    boolean isCredentialsNonExpired();
    //账号是否启用
    boolean isEnabled();
}

我们需要将数据库中查询到的用户信息,封装成一个UserDetails对象并交给Spring Security,因此我们还需要借助另一个类:UserDetailsService,该接口中仅有一个方法loadUserByUsername,专门用于基于用户名从数据库中查询用户信息。

public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
      自定义UserDetailsService
//service层创建一个UserDetailsService接口的实现类
@Service
public class UserServiceImpl implements UserDetailsService {
    //这里要注入mapper层从数据库获取用户信息,这里模拟一下用户
    private List<String> users= Arrays.asList("xiaowu","xiaoli");
    //模拟分配的角色
    private static HashMap<String, String[]> AUTHORITIES=new HashMap<>();
    static {
        AUTHORITIES.put("xiaowu",new String[]{"ROLE_USER","ROLE_ADMIN"});
        AUTHORITIES.put("xiaoli",new String[]{"ROLE_USER"});
        AUTHORITIES.put("xiaoming",new String[]{"ROLE_USER","ROLE_ADMIN"});
    }
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //基于用户名查询用户对象,如果用户不存在抛出异常
        if(!users.contains(username)){
            throw new UsernameNotFoundException("用户名或密码错误!");
        }
        //如果查到了,返回的对象必须是UserDetails接口的实现类
          //--可以通过Spring Security提供的User类来创建,将数据库查询到的用户名、密码、角色等封装进去
        String password = "$2a$10$YvBKyAXzeuJTtApJqD4jCOWZYAznZcXhurF7321zKmPXBlYqJJcaC"; //123456加密后的密码
        UserDetails details = User.withUsername(username)//用户名
                .password(password) //密码
                .authorities(AUTHORITIES.get(username))
                .build();//权限信息
        return details;
    }
}

前提是在配置类中加入:

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

四、配置记住我功能

在自己定义的配置类中依赖注入刚刚创建的UserDetailsService并书写以下代码

//在配置类中依赖注入自定义的UserServiceImpl   
@Autowired
    private UserServiceImpl userService;

//在configure方法中添加
     http.rememberMe() //开启记住我功能
                .rememberMeParameter("remember-me") //默认记住我参数名,也就是提交表单时候的属性名
                .tokenValiditySeconds(60*60*24) //默认记住我有效时间,单位秒,默认2周
                .userDetailsService(userService);   //在用户第一次访问时,会调用自定义的UserDetailsService的loadUserByUsername方法,根据用户名查询用户信息,并返回给框架,框架会做密码校验,如果密码正确,则登录成功,否则登录失败。
       

五、获取用户认证信息

        登录成功后,有时候需要获取到认证后的用户信息,那么此时我们需要接触到两个新的对象

SecurityContextHolder和Authentication,Security中有SecurityContext,该对象封装了Security应用的上下文数据,并且该对象可以通过工具类。SecurityContextHolder访问到,因此让我们使用Security可以变得更加简单一点。

Authentication代表的是认证信息对象,当一个用户认证通过以后。就会封装成改接口的实现类,存入到SecurityContext中,该对象包含用户认证后的所有信息,因此我们在开发的时候经常使用。

public interface Authentication extends Principal, Serializable {
    //当前用户拥有的权限列表,每一个权限对象都必须是GranteAuthority的实现类,不过大部分情况下都是只需要保存一个字符串。
    Collection<? extends GrantedAuthority> getAuthorities();
    //凭证信息,在用户名密码认证场景下,等同 于用户密码,在用户认证成功后为了保证安全性,这个值会被删除。
    Object getCredentials();
    //认证用户的详细信息,通常为webAuthenticationDetails接口的实现类,保存了用户的ip、sessionId等信息
    Object getDetails();
    //主体身份信息,再认证通过后通常都是userDetails接口的实现类对象,可以理解为UserDtailsService所返回的哪个对象
    Object getPrincipal();
    //是否已认证,只有返回true才表示当前用户是已经通过认证了的
    boolean isAuthenticated();
    //设置是否已认证属性
    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

 Spring Security给我们提供了很多种获取用户信息的方式,可以在Controller的方法中直接注入Authentication对象,甚至可以通过请求对象获取用户凭证对象,但是我们推荐的还是通过SecurityContextHolder 工具类来获取,对其进行再次封装工具类,可以直接使用。

public class SecurityUtils {

    /**
     * 获取用户
     */
    public static UserDetails getLoginUser() {
        Object principal = getAuthentication().getPrincipal();
        //这里判断是为了防止不走认证就直接访问页面的情况,不然会报空指针异常
        if(principal instanceof UserDetails){
            return (UserDetails) principal;
        }
        return null;
    }

    /**
     * 获取Authentication
     * @return
     */
    private static Authentication getAuthentication() {
            return SecurityContextHolder.getContext().getAuthentication();
    }

    /**
     *使用Bcrypt算法加密密码
     */
    public static String encryptPassword(String password) {
        return new BCryptPasswordEncoder().encode(password);
    }
}
  // http://127.0.0.1:8080/user
    @RequestMapping("/user")
    @ResponseBody
    public User user() {
        UserDetails user1 = SecurityUtils.getLoginUser();
        System.out.println(user1.getUsername());
        System.out.println(user1.getPassword());
        Collection<? extends GrantedAuthority> authorities = user1.getAuthorities();  
                 authorities.stream().map(GrantedAuthority::getAuthority).forEach(System.out::println);
    }

打印如图所示:

六、用户的认证鉴权改进

回顾一下登录的逻辑

基于Spring Security认证流程如下图:

在之前案例演示中,只能用于前后端不分离的场景,而当我们开发前后端分离项目,需要后端返回的是数据而不是页面,这时候就会存在一定问题了,其实我们可以通过自己提供的登录接口,以及自定义登录失败处理器的的方案实现返回数据的方式。

标签:用户名,登录,Spring,用户,认证,Security,public
From: https://blog.csdn.net/wuguanghui1/article/details/144468956

相关文章

  • springboot毕设 中医药院校科研会议系统 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着中医药事业的蓬勃发展,中医药院校在科研领域的交流与合作日益频繁。传统的会议组织与管理方式往往依赖于人工操作,不仅效率低下,而且容易出错,难以满......
  • springboot毕设 装饰工程管理系统——质量管理子系统 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着城市化进程的加快和居民生活水平的提高,装饰工程行业迎来了前所未有的发展机遇。然而,装饰工程行业的快速发展也带来了诸多挑战,尤其是质量管理方面......
  • springboot毕设 在线项目管理 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展,项目管理已经逐渐从传统的手工记录、纸质文档管理向数字化、在线化转型。在线项目管理作为现代企业管理的重要组成部分,通过......
  • springboot毕设 在线学习平台 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展,教育领域正经历着深刻的变革。在线学习作为新兴的教育模式,以其便捷性、灵活性和资源丰富性,日益受到广大师生的青睐。特别是......
  • 基于SpringBoot在线音乐系统平台功能实现三
    一、前言介绍:1.1项目摘要随着互联网技术的迅猛发展和普及,人们对音乐的获取和欣赏方式发生了巨大改变。传统的音乐播放方式,如CD、磁带或本地下载的音乐文件,已经不能满足用户日益增长的需求。用户更希望通过网络直接获取各种类型的音乐,并享受随时随地的音乐播放服务。现代......
  • 基于SpringBoot在线音乐系统平台功能实现四
    一、前言介绍:1.1项目摘要随着互联网技术的迅猛发展和普及,人们对音乐的获取和欣赏方式发生了巨大改变。传统的音乐播放方式,如CD、磁带或本地下载的音乐文件,已经不能满足用户日益增长的需求。用户更希望通过网络直接获取各种类型的音乐,并享受随时随地的音乐播放服务。现代......
  • springboot毕设 中华传统文化学习平台 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在全球化日益加深的今天,中华传统文化作为中华民族的瑰宝,不仅承载着丰富的历史底蕴,更蕴含着深邃的哲学思想与人文精神。然而,随着科技的飞速发展和生活......
  • springboot毕设 酒店综合服务程序+论文
    系统程序文件列表开题报告内容研究背景随着旅游业的蓬勃发展,酒店作为旅游产业链中的重要一环,其服务质量与综合功能的完善程度直接影响着游客的旅行体验。在当今竞争激烈的市场环境中,酒店不仅需要提供舒适的住宿环境,还需通过多元化的服务来满足不同游客的个性化需求。因此,构......
  • springboot毕设 旅行分享系统程序+论文
    系统程序文件列表开题报告内容研究背景随着全球化的加速发展和人们生活水平的提高,旅行已成为现代人休闲娱乐、拓宽视野的重要方式之一。在信息化时代背景下,旅行信息的获取与分享变得尤为重要。当前市场上虽然已存在众多旅行服务平台,但大多数平台侧重于提供单一的预订服务或......
  • COM398 Systems Security
    COM398SystemsSecurity60%OFTHETOTAL MARKSubmission Date:                           9th  December 2024 (12:00(Noon) UK time)Date returnedwithfeedback:    Withintwentyworkingdaysafter the submission dea......