SpringSecurity详解

本文详细介绍Spring Security的实现方式,包括登录验证流程、权限控制配置,并演示如何自定义登录页面及处理器。此外,还介绍了如何通过注解进行权限控制,并配置RememberMe功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringSecurity详解

  • 登录实现:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class userDetailService implements UserDetailsService {
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        System.out.println("*******************************");
        //1.根据用户名去数据库查询,不存在则抛出异常UsernameNotFoundException
        if (!"admin".equals(username)) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        //2.比较密码(注册时已经加密) 如果成功返回userDetails
        String password = passwordEncoder.encode("123");
        return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"));
    }
}
  • 通过配置进行权限控制
import com.song.handler.MyAccessDeniedHandler;
import com.song.handler.MyAuthenticationFailureHandler;
import com.song.handler.MyAuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private  MyAccessDeniedHandler myAccessDeniedHandler;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //自定义登陆页面
        http.formLogin()
            //自定义入参
            .usernameParameter("username11")//Spring默认的都是username,如果想更换则需要配置usernameParameter,不配置会报错
            //自定义登陆页面
            .loginPage("/login.html")
            //必须和表单提交的接口一样,执行自定义登录逻辑
            .loginProcessingUrl("/login")
            //登陆成功跳转
            //.successForwardUrl("/toLogin")
            //自定义登陆成功处理器(源码中是转发,而我们自定义使用的是重定向)
            .successHandler(new MyAuthenticationSuccessHandler("http://www.baidu.com"))
            //自定义登陆失败处理器
            .failureHandler(new MyAuthenticationFailureHandler("http://www.baidu.com"));
            //.failureForwardUrl("/toError");
        //授权
        http.authorizeRequests()
            //放行,不需要认证
            .antMatchers("/login.html").permitAll()
            .antMatchers("/error.html").permitAll()
            //基于权限控制,严格区分大小写
            //.antMatchers("/main1.html").hasAuthority("admin")
            .antMatchers("/main1.html").hasAnyAuthority( "adN")
            //所有的请求都要认证,必须登录(anyRequest需要放在最后,否则会直接报错)
            //基于角色判断(需要注意 在授权的时候需要写ROLE_xxx)认证的时候就是abc
            .antMatchers("/main1.html").hasRole("abc")
            //基于IP地址
            //.antMatchers("/main1.html").hasIpAddress("127.0.0.1")
            .anyRequest().authenticated();

        //异常处理
        http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);
        //关闭csrf防护
        http.csrf().disable();
    }

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

  • 通过注解进行权限控制

    需要开启注解,因为Spring默认是关闭注解进行权限控制的
    @EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法注解
        在启动类前加或者在SecurityConfig配置类里面加
        Secured 在角色控制的时候必须使用ROLE_xxx (以Role开头)
    
  • 自定义403页面

    import java.io.IOException;
    import java.io.PrintWriter;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.web.access.AccessDeniedHandler;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyAccessDeniedHandler implements AccessDeniedHandler {
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
            //自定义403页面
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.setHeader("Content-Type", "application/json;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write("{\"status\":\"error\",\n \"msg:\" \"权限不足,请联系管理员\"}");
            writer.flush();
            writer.close();
        }
    }
    
  • 配置rememberme

    1. 导入依赖

      <!--使用SpringSecurity中的 RememberMe 需要导入 mybatis 和 mysql 数据库依赖-->
              <dependency>
                  <groupId>org.mybatis.spring.boot</groupId>
                  <artifactId>mybatis-spring-boot-starter</artifactId>
                  <version>2.1.1</version>
              </dependency>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.18</version>
              </dependency>
      
    2. 配置Jdbc

      spring:
        datasource:
          url: "jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC"
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
      
    3. security配置

      import com.song.handler.MyAccessDeniedHandler;
      import com.song.service.UserDetailServiceImpl;
      import javax.sql.DataSource;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
      import org.springframework.security.config.annotation.web.builders.HttpSecurity;
      import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
      import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
      import org.springframework.security.crypto.password.PasswordEncoder;
      import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
      import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
      
      @Configuration
      @EnableGlobalMethodSecurity(prePostEnabled = true)//开启注解控制权限
          @Autowired
          private DataSource dataSource;
          @Autowired
          private PersistentTokenRepository persistentTokenRepository;
          @Autowired
          private UserDetailServiceImpl userDetailService;
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              //配置RememberMe
              http.rememberMe()
                  .tokenRepository(persistentTokenRepository)
                  //超时时间,默认2周
                  .tokenValiditySeconds(60)
                  //自定义登录逻辑
                  .userDetailsService(userDetailService);
      
      
              //退出登录
              http.logout()
                  //退出成功后跳转的页面
                  .logoutSuccessUrl("/login.html")
                  .logoutUrl("/logout");
          }
      
      
          @Bean
          public PersistentTokenRepository persistentTokenRepository() {
              JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
              //设置数据源
              jdbcTokenRepository.setDataSource(dataSource);
              //自动建表(第一次启动时开启,第二次启动时注释掉,因为第一次建表,第二次存在就报错)
      //        jdbcTokenRepository.setCreateTableOnStartup(true);
              return jdbcTokenRepository;
          }
      
      
      *********************************************************************************
          
          
          
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.security.core.authority.AuthorityUtils;
      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.security.crypto.password.PasswordEncoder;
      import org.springframework.stereotype.Service;
      
      @Service
      public class UserDetailServiceImpl implements UserDetailsService {
          @Autowired
          private PasswordEncoder passwordEncoder;
      
          @Override
          public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
      
              System.out.println("来来来");
              //1.根据用户名去数据库查询,不存在则抛出异常UsernameNotFoundException
              if (!"admin".equals(username)) {
                  throw new UsernameNotFoundException("用户名不存在");
              }
              //2.比较密码(注册时已经加密) 如果成功返回userDetails
              String password = passwordEncoder.encode("123");
              return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_abc"));
          }
      }
      
### 图片加载错误 `net::ERR_CONTENT_LENGTH_MISMATCH` 的原因分析 当浏览器尝试加载图片资源时,如果返回的状态码为 `206 Partial Content` 并伴随错误提示 `net::ERR_CONTENT_LENGTH_MISMATCH`,这通常表明服务器响应的内容长度与实际传输的数据不匹配[^1]。以下是可能的原因及其解决方案: #### 可能原因 1. **网络中断或不稳定** 如果在网络传输过程中发生断开或其他异常情况,可能导致部分数据丢失,从而引发该问题。 2. **缓存机制冲突** 浏览器可能会基于 HTTP 缓存策略请求部分内容(Range Requests),而某些服务器配置不当会误处理这些请求,导致内容长度不符。 3. **Web Server 配置问题** Web 服务器(如 Nginx 或 Apache)可能存在未正确设置 Range 请求支持的情况,或者文件被截断[^2]。 4. **CDN 性能问题** 使用 CDN 加速的情况下,CDN 节点可能出现性能瓶颈或配置错误,影响静态资源的正常分发。 --- ### 解决方案 #### 方法一:优化 Web 服务器配置 对于使用 Nginx 的场景,可以调整其配置以更好地支持范围请求(Range Requests)。例如,在 Nginx 中启用以下选项: ```nginx http { ... proxy_buffering off; } ``` 此配置可防止代理缓冲区过早关闭连接而导致的部分内容缺失[^3]。 #### 方法二:禁用 Range 请求 如果确认问题是由于 Range 请求引起的,则可以通过修改 `.htaccess` 文件来禁用它(适用于 Apache 环境): ```apache <IfModule mod_headers.c> Header unset Accept-Ranges </IfModule> ``` #### 方法三:检查并修复源文件 确保上传到生产环境中的图片文件本身没有损坏。可以在本地验证文件完整性后再部署至线上服务。 #### 方法四:更新客户端代码逻辑 前端开发人员应考虑增加重试机制以及更健壮的错误捕获功能。比如通过 JavaScript 实现如下增强型图像加载函数: ```javascript function loadImage(src, callback) { const img = new Image(); let retries = 3; function attemptLoad() { img.onload = () => callback(null, img); img.onerror = (err) => { if (--retries > 0) { console.warn('Retrying image load...'); setTimeout(attemptLoad, 500); // 延迟半秒后重新尝试 } else { callback(err, null); } }; img.src = src; // 设置新的src属性触发加载过程 } attemptLoad(); // 初次调用 } // Usage Example: loadImage('/path/to/image.jpg', (error, result) => { if (!error && result instanceof HTMLImageElement) { document.body.appendChild(result); } else { console.error('Failed to load the image:', error); } }); ``` 上述方法能够有效减少因短暂性网络波动带来的失败风险[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

blog_xsong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值