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"));
          }
      }