springsecurity学习二(代码片段)

nxzblogs nxzblogs     2022-12-06     542

关键词:

doc:https://docs.spring.io/spring-security/site/docs/

基于表单的认证(个性化认证流程):

一、自定义登录页面

1、在securityConfigy配置类中的config方法中添加调用链方法

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http.formLogin()//指定是表单登录
                .loginPage("/cus_login.html")//登录页面
                .and()
                .authorizeRequests()//授权
                .anyRequest()//任何请求
                .authenticated();//都需要身份认证
    

2、同时在resources/resources下创建一个html文件

技术图片

 

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

自定义登录页面
</body>
</html>

/3、配置好之后,启动项目,访问localhost:9999/h  api

浏览器后报错:,重定向到登录页面次数太多

技术图片

4、为什么出现这种情况:

  因为在securityconfigy配置类中指定了一个loginPage,但是在方法链中,有表明:任何请求都需要认证

.anyRequest()//任何请求
                .authenticated();//都需要身份认证

处理:在anyRequest方法前和authorizeRequests方法后添加antMatchers匹配规则,指定登录页面允许访问

.authorizeRequests()//授权
                .antMatchers("/cus_login.html").permitAll()

配置完成后再次访问localhost:9999/h  :,即可跳转至自定义的登录页面

技术图片

5、完善登录页面

//在usernamePasswordAuthenticationFilter中处理的action为/login,请求方式是post   
public UsernamePasswordAuthenticationFilter() super(new AntPathRequestMatcher("/login", "POST"));
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
自定义登录页面
<form action="/authentication/form" method="POST">
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="text" name="password"></td>
        </tr>
        <tr>
            <td>
                <button type="submit">登录</button>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

 因为在登录页面中设定的action为“/authentication/form”,所以在securityConfig配置类的方法链中添加loginProcessingUrl方法

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http.formLogin()//指定是表单登录
                .loginPage("/cus_login.html")//登录页面
                .loginProcessingUrl("/authentication/form")
                .and()
                .authorizeRequests()//授权
                .antMatchers("/cus_login.html").permitAll()
                .anyRequest()//任何请求
                .authenticated();//都需要身份认证
    

重启项目:访问localhost:9999/h

技术图片

显示为自定义的页面。

csrf().disable();//跨站防护不适用

技术图片

6、将loginPage(/cus_login.html) html页面改为一个controller代码

①新建一个controller

@RestController
public class LoginSecurityController 

    @RequestMapping("/authentication/require")
    public String requireAuthentication(HttpServletRequest request, HttpServletResponse response) 
        

        return null;
    

②修改securityconfig配置类中的config中的方法调用链中的loginPage

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http.formLogin()//指定是表单登录
                .loginPage("/authentication/require")//登录页面
                .loginProcessingUrl("/authentication/form")
                .and()
                .authorizeRequests()//授权
                .antMatchers("/authentication/require").permitAll()
                .anyRequest()//任何请求
                .authenticated()//都需要身份认证
                .and()
                .csrf().disable();//跨站防护不适用
    

 

二、自定义登录成功的处理

 实现AuthenticationSucessHandler接口

 

三、自定义登录失败的处理

实现AuthenticationFailureHandler接口

 

四、源码学习

技术图片

技术图片

1、认证流程说明

登录请求进来是,会先到UsernamePasswordAuthentication类的,其实最先走的是它的父类AbstractAuthenticationProcessingFilter.java,在父类中会走attemptAuthentication方法,父类没有实现,因此会走子类的方法,当认证成功后,会走到最后successFulAuthentication方法

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException 

     。。。。
     。。。。 Authentication authResult;
try authResult = attemptAuthentication(request, response); if (authResult == null) // return immediately as subclass has indicated that it hasn‘t completed // authentication return; sessionStrategy.onAuthentication(authResult, request, response);     。。。。     。。。。 // Authentication success if (continueChainBeforeSuccessfulAuthentication) chain.doFilter(request, response); successfulAuthentication(request, response, chain, authResult);

attemptAuthentication方法(子类usernamepasswordAuthenticationFilter.java)

    public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException 
        if (postOnly && !request.getMethod().equals("POST")) 
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        

        String username = obtainUsername(request);
        String password = obtainPassword(request);
    。。。。
    。。。。
username = username.trim();     //这个UsernamepasswordAuthenticationToken其实就是封装了username 和password信息 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); // 这个setDetails会把请求的一些信息设置到authRequest对象中 setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest);

上边那个this.getAuthenticationManager() 会获取一个authenticationManager类

作用:收集相关的provider,当请求到的时候,循环判断是否支持当前的provider类型

public interface AuthenticationManager 
  //authenticate认证方法,交给实现类实现 Authentication authenticate(Authentication var1)
throws AuthenticationException;

他的实现类(用的就是ProviderManger类),不同的provider支持的Authentication是不同的

技术图片

 

例如:UsernamePasswordAuthenticationToken类型的Authentication时,provider就是DaoAuthenticationProvider,

 

   public Authentication authenticate(Authentication authentication) throws AuthenticationException 
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        AuthenticationException parentException = null;
        Authentication result = null;
        Authentication parentResult = null;
        boolean debug = logger.isDebugEnabled();
        Iterator var8 = this.getProviders().iterator();

        while(var8.hasNext()) 
            AuthenticationProvider provider = (AuthenticationProvider)var8.next();
            if (provider.supports(toTest)) 
                if (debug) 
                    logger.debug("Authentication attempt using " + provider.getClass().getName());
                

                try 
                    result = provider.authenticate(authentication);
                    if (result != null) 
                        this.copyDetails(authentication, result);
                        break;
                    
                 catch (AccountStatusException var13) 
                    this.prepareException(var13, authentication);
                    throw var13;
                 catch (InternalAuthenticationServiceException var14) 
                    this.prepareException(var14, authentication);
                    throw var14;
                 catch (AuthenticationException var15) 
                    lastException = var15;
                
            
        

      。。。。。
    

上边那个标红的provider.authenticate方法会走到DaoAuthenticationProvider对象的父类对象的authticate方法,

 

public Authentication authenticate(Authentication authentication) throws AuthenticationException 
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> 
            return this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported");
        );
        String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName();
        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);
        if (user == null) 
            cacheWasUsed = false;

            try 
         //这块会进入子类DaoAuthenticationPrivoder user
= this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication); catch (UsernameNotFoundException var6) this.logger.debug("User ‘" + username + "‘ not found"); if (this.hideUserNotFoundExceptions) throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); throw var6; Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");        try
       //校验是否锁定。。(userdetails里边的几个返回值)
this.preAuthenticationChecks.check(user);
       //校验密码是否匹配
this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication); catch (AuthenticationException var7) if (!cacheWasUsed) throw var7; cacheWasUsed = false; user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication)
       this.preAuthenticationChecks.check(user); this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);      //后置查询  this.postAuthenticationChecks.check(user); if (!cacheWasUsed) this.userCache.putUserInCache(user); Object principalToReturn = user; if (this.forcePrincipalAsString) principalToReturn = user.getUsername(); return this.createSuccessAuthentication(principalToReturn, authentication, user);

子类DapAuthenticationPrivoder类的

    protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException 
        this.prepareTimingAttackProtection();

        try 
       //这一块就是调用自定义得人UserDetailsService类 UserDetails loadedUser
= this.getUserDetailsService().loadUserByUsername(username); if (loadedUser == null) throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation"); else return loadedUser; catch (UsernameNotFoundException var4) this.mitigateAgainstTimingAttack(authentication); throw var4; catch (InternalAuthenticationServiceException var5) throw var5; catch (Exception var6) throw new InternalAuthenticationServiceException(var6.getMessage(), var6);

进入自定义的CusUserDetailsService

public class MyUserDetailsService implements UserDetailsService 
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException 
        //根据username查找用户信息,在这,先手动写一个user信息
        log.info("查找用户信息", s);

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String password = encoder.encode("password");

        //user前两个参数是进行认证的,第三个参数是当前用户所拥有的权限,security回根据授权代码进行验证
        return new User(s, password, true, true, true, true,
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
//        AuthorityUtils.commaSeparatedStringToAuthorityList 这个方法是将一个字符一“,” 分割开
    

技术图片

 

二初始化springsecurity(代码片段)

一、初始化SpringSecurity1.1、SpringSecurity概念SpringSecurity是spring采用AOP思想,基于servlet过滤器实现的安全框架。它提供了完善的认证机制和方法级的授权功能。是一款非常优秀的权限管理框架。1.2、SpringSecurity简单入门SpringSecurit... 查看详情

二初始化springsecurity(代码片段)

一、初始化SpringSecurity1.1、SpringSecurity概念SpringSecurity是spring采用AOP思想,基于servlet过滤器实现的安全框架。它提供了完善的认证机制和方法级的授权功能。是一款非常优秀的权限管理框架。1.2、SpringSecurity简单入门SpringSecurit... 查看详情

二springsecurity基本原理(代码片段)

一、SpringSecurity常见过滤器介绍SpringSecurity本质是一个过滤器链从启动时,可以获取到过滤器链:SpringSecurity中过滤器的介绍:org.springframework.security.web.context.SecurityContextPersistenceFilter首当其冲的一个过滤器,作用之... 查看详情

springsecurity学习(代码片段)

1.SpringSecurity框架简介1.1概要Spring是非常流行和成功的Java应用开发框架,SpringSecurity正是Spring家族中的成员。SpringSecurity基于Spring框架,提供了一套Web应用安全性的完整解决方案。正如你可能知道的关于安全方面的两个主要区域是... 查看详情

springsecurity学习——快速开始(代码片段)

前言SpringSecurity是目前Java后台管理系统中使用最常见的安全认证框架之一。它的上手没Shiro那么直观。Shiro是可以直接开个javaSE应用验证的,但SpringSecurity基本要求和web关联起来。刚开始看甚至不知道从何入手。最近决定静下... 查看详情

springsecurity学习——快速开始(代码片段)

前言SpringSecurity是目前Java后台管理系统中使用最常见的安全认证框架之一。它的上手没Shiro那么直观。Shiro是可以直接开个javaSE应用验证的,但SpringSecurity基本要求和web关联起来。刚开始看甚至不知道从何入手。最近决定静下... 查看详情

springboot集成springsecurity——入门程序(代码片段)

因为项目需要,第一次接触SpringSecurity,早就听闻SpringSecurity功能强大但上手困难,学习了几天出入门道,特整理这篇文章希望能让后来者少踩一点坑(本文附带实例程序,请放心食用)本篇文章环境:SpringBoot2.0+Mybatis+SpringSecurit... 查看详情

springsecurity学习总结(代码片段)

这几天一直在学习springsecurity的相关知识.逛各大论坛,看相关api与教学视频,获益良多!简介SpringSecurity是为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了完整的安全性解决方案,可以在Web... 查看详情

springsecurity学习——密码加密(代码片段)

前言上一篇文章我们实现了从数据库读取用户名密码到SpringSecurity中,并验证登录成功。不过密码的形式有点奇怪,这篇文章我们研究一下密码加密和比对的问题。SpringSecurity的密码加密和比对密码编码器的使用SpringSecurit... 查看详情

安全框架springsecurity是什么?如何理解springsecurity的权限管理?(代码片段)

大家好,我是卷心菜。本篇主要讲解SpringSecurity的基本介绍和架构分析,如果您看完文章有所收获,可以三连支持博主哦~,嘻嘻。文章目录一、前言二、SpringSecurity简介三、权限管理1、什么是权限管理2、什么是认... 查看详情

springsecurity学习(代码片段)

认证和授权认证解决“我是谁的问题”什么是认证?认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪一个用户授权解决“我能做什么”的问题授权:经过认证之后判断当前用户是否有权限进... 查看详情

springsecurity-用户动态授权及动态角色权限(代码片段)

一、SpringSecurity动态授权上篇文章我们介绍了SpringSecurity的动态认证,上篇文章就说了SpringSecurity的两大主要功能就是认证和授权,既然认证以及学习了,那本篇文章一起学习了SpringSecurity的动态授权。上篇文章地址... 查看详情

java之springboot+springsecurity+vue实现后台管理系统的开发二后端(代码片段)

Java之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【一、前端】跳转Java之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【二、后端】跳转Java之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【三、系统权限... 查看详情

springsecurity源码:整体框架设计(代码片段)

...瞅三、框架接口设计建造者配置器四、总结五、系列文章SpringSecurity系列SpringSecurityOAuth系列一、一句话概括框架原理整个框架的核心就是构建一个名字为springSecurityFilterChain的过滤器Bean,它的类型是FilterChainProxy。二、何处开... 查看详情

springsecurity学习——快速开始(代码片段)

前言SpringSecurity是目前Java后台管理系统中使用最常见的安全认证框架之一。它的上手没Shiro那么直观。Shiro是可以直接开个javaSE应用验证的,但SpringSecurity基本要求和web关联起来。刚开始看甚至不知道从何入手。最近决定静下... 查看详情

springsecurity学习——使用数据库保存密码(代码片段)

前言上一篇文章我们已经快速搭起SpringSecurity应用。我们使用的是默认的用户名密码。通常项目中我们使用数据库保存密码,并且数据库的密码是加密保存的。如何做到呢,是这篇文章的重点。引入数据库新建数据库表我... 查看详情

springsecurity-用户动态授权及动态角色权限(代码片段)

一、SpringSecurity动态授权上篇文章我们介绍了SpringSecurity的动态认证,上篇文章就说了SpringSecurity的两大主要功能就是认证和授权,既然认证以及学习了,那本篇文章一起学习了SpringSecurity的动态授权。上篇文章地址... 查看详情

轻松上手springsecurity,oauth,jwt(代码片段)

目录学习目标一.SpringSecurity1.SpringSecurity简介及快速入门<1>.SpringSecurity简介<2>.SpringSecurity快速入门2.SpringSecurity基本原理<1>.UserDetailsService详解<2>.PasswordEncoder密码解析器详解3.SpringSecurity自定义 查看详情