• Spring Security是什么? - 简单例子(三)


    2、spring security中,安全配置通过继承WebSecurityConfigurerAdapter来配置

    @Configuration
    public class MyWebSecurityConfigurerAdapter  extends WebSecurityConfigurerAdapter{
        protected void configure(HttpSecurity http) throws Exception {
        	//做大量的配置
        	//认证配置
    	//授权配置
    // 例如:
                    http
                    .authorizeRequests()
                    //访问"/"和"/home"路径的请求都允许
                    .antMatchers("/", "/home","/staff","/staff/*")
                    .permitAll()
                    //而其他的请求都需要认证
                    .anyRequest()
                    .authenticated()
                    .and()
                    //修改Spring Security默认的登陆界面
                    .formLogin()
                    .loginPage("/login")
                    .permitAll()
                    .and()
                    .logout()
                    .permitAll();
    
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3、添加认证校验 (authentication)

    • 创建数据库,创建一个用户库,用户名和密码

      在这里插入图片描述

    • 创建访问数据库的接口

      public interface UserMapper {
          User selectByUserName(String userName);
      
          List selectAllRoleByUserId(Integer userId);
      
          List selectPermissionsByUserId(Integer userId);
      
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • 创建访问数据库的userMapper.xml

      
      
      
          
          
          
      
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    • 新建一个类,实现spring security提供的接口org.springframework.security.core.userdetails.UserDetailsService

      这样就会调用该类的方法去访问数据库认证了。这一步中会返回一个User对象,该对象是SpringSecurity里定义的User对象

      需要将用户的权限和角色用逗号分割后组装在一起。

      @Service
      public class UserDetailsServiceImpl implements UserDetailsService {
      
          @Autowired
          private PasswordEncoder passwordEncoder;
      
          @Autowired
          private UserMapper userMapper;
      
          @Override
          public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
      
              com.demo.entity.User user = userMapper.selectByUserName(s);
              if (user == null) {
                  throw new UsernameNotFoundException("用户不存在");
              }
      
              List roles = userMapper.selectAllRoleByUserId(user.getId());
              List permissions = userMapper.selectPermissionsByUserId(user.getId());
      
              StringBuilder sb = new StringBuilder();
              for (String role : roles) {
                  sb.append("ROLE_" + role + ",");
              }
              for (String permission : permissions) {
                  sb.append(permission + ",");
              }
      
              String rolepermission = sb.substring(0, sb.length() - 1);
      
              UserDetails userDetails = new User(user.getUserName(), user.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(rolepermission));
              return userDetails;
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
    • 注意,

      • 认证的步骤,是先获取用户信息,再校验用户密码的认证过程。

      • 在上步骤中需要使用到一个passwordEncoder编码器,来生成加密密码明文生成密文。

    • 通过java类来配置一个passwordEncoder

      @Configuration
      public class SecurityConfig {
          @Bean
          protected PasswordEncoder passwordEncoder() {
              return new BCryptPasswordEncoder();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    4、添加配置,用于完成认证流程管理

    • 登录的界面:/showLogin

    • 登录逻辑入口 /login

    • 登录成功地址 /showMain

    • 登录失败地址 /showFail

    • .usernameParameter("myusername")以及.passwordParameter("mypassword")定义了登录页面中的表单名称是myusernamemypassword

    UsernamePasswordAuthenticationFilter :对/login 的 POST 请求做拦截,校验表单中用户名,密码。

    通过源码不难发现:一旦我们使用SpringSecurity来作为认证框架,我们要写自己的登录的接口的话,一定要用post请求,并且用户名和密码是固定的,只能用username和password来作为参数名,因为这是SpringSecurity默认的(如果执意要改,可以通过配置文件进行改)。

    public class MyWebSecurityConfigurerAdapter  extends WebSecurityConfigurerAdapter{
        protected void configure(HttpSecurity http) throws Exception {
    		
    	       http.formLogin()
                    //未登录时的地址
                    .loginPage("/showLogin")
                    //处理登录请求的url
                    .loginProcessingUrl("/login")
                    //.successForwardUrl("/showMain")
                    //认证成功后跳转的地址
                    .successHandler(new AuthenticationSuccessHandler() {
                        @Override
                        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                            httpServletResponse.sendRedirect("/showMain");
                        }
                    })
                    //.failureForwardUrl("/showFail")
                    //登录失败后的处理器
                    .usernameParameter("myusername")
                    //客户端的密码参数名称
                    .passwordParameter("mypassword")
                    .failureHandler(new AuthenticationFailureHandler() {
                        @Override
                        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                            httpServletResponse.sendRedirect("/showFail");
                        }
                    });
    
                        http.exceptionHandling()
                    //.accessDeniedHandler(accessDeniedHandler);
                    //只适用于非前端框架,适用于同步请求的方式
                    //如果是异步请求需要使用上一种方式。
                    .accessDeniedPage("/showAccessDenied");
    
              }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    5、异常配置

    配置异常页面,如果程序异常测跳转到该页面。

    如果是异步请求,如ajax,则可以实现AccessDeniedHandler 接口

     @Component
    public class MyAccessDeniedHandler implements AccessDeniedHandler {
        @Override
        public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            httpServletResponse.setStatus(httpServletResponse.SC_FORBIDDEN);
            PrintWriter writer = httpServletResponse.getWriter();
            writer.println("权限不足");
            writer.flush();
            writer.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    6、配置 Remember Me

    需要配置生成一个token,并将token下发给浏览器。
    因此需要一个PersistentTokenRepository类。
    通过java配置类的方式来定义bean,这个bean就是persistentTokenRepository

    @Configuration
    public class RememberMeConfig {
        @Autowired
        private DataSource dataSource;
    
        @Bean
        protected PersistentTokenRepository getPersistentTokenRepository(){
            JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
            //jdbcTokenRepository.setCreateTableOnStartup(true);
            jdbcTokenRepository.setDataSource(dataSource);
            return jdbcTokenRepository;
        }
    }
    // 在MyWebSecurityConfigurerAdapter中配置.rememberMe()
        private MyAccessDeniedHandler accessDeniedHandler;
        private PersistentTokenRepository persistentTokenRepository;
        protected void configure(HttpSecurity http) throws Exception {
            //其他配置
            http.rememberMe()
                .userDetailsService(userDetailsService)
                .tokenRepository(persistentTokenRepository)
                .tokenValiditySeconds(10*2);
        	//其他配置
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    7、配置授权

    授权一般需要依托用户的角色,对权限集合进行绑定。

    • 因为必须先登录,因此对于登录页面直接方行,antMatchers(“/showLogin”,“/showFail”).access(“permitAll”)。

    • 因为静态资源也要直接放行,所以对于.antMatchers(“/images/**”).permitAll() .regexMatchers(“/js/.*”).permitAll() .antMatchers(“/demo”).permitAll()这几个直接放行。

    • .antMatchers(“/abc”).denyAll() 所有到/abc的请求都拒绝。

    • 最后一行的.anyRequest().authenticated(); 声明所有的请求都需要登录。

    public class MyWebSecurityConfigurerAdapter  extends WebSecurityConfigurerAdapter{
        protected void configure(HttpSecurity http) throws Exception {
    				
    				//前面的认证配置
    				
    		http.authorizeRequests()
                    //.antMatchers("/showLogin","/showFail").permitAll()
                    .antMatchers("/showLogin","/showFail").access("permitAll")
                    //对于静态和动态请求需要分开
                    //.antMatchers("/js/**").permitAll()
                    .antMatchers("/abc").denyAll()
                    .antMatchers("/jczl").hasAnyAuthority("demo:update")
                    //.antMatchers("/jczl").hasAnyRole("ADMIN")
                    .antMatchers("/admin").access("@myServiceImpl.hasPermission(request,authentication)")
                    .antMatchers("/ip").hasIpAddress("192.168.7.86")
                    .antMatchers("/images/**").permitAll()
                    .regexMatchers("/js/.*").permitAll()
                    .antMatchers("/demo").permitAll()
                    .anyRequest().authenticated();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    8、配置安全 csrf

    为防止跨越的表单提交,在login.html 的隐藏域中需要加上

    然后在MyWebSecurityConfigurerAdapter 中继续取消 http.csrf().disable();的注释,即默认开启csrf的校验了。

    WebSecurityConfigurerAdapter类:可以通过重载该类的三个configure()方法来制定Web安全的细节。

    1、configure(WebSecurity):通过重载该方法,可配置Spring Security的Filter链。

    2、configure(HttpSecurity):通过重载该方法,可配置如何通过拦截器保护请求。

    3、configure(AuthenticationManagerBuilder):通过重载该方法,可配置user-detail(用户详细信息)服务。

    • 1、使用基于内存的用户存储:

    • 2、基于数据库表进行认证:

    \*\* 3、基于LDAP进行认证 \*\*
    
    • 1
    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        /**
         * 自定义用户认证逻辑
         */
        @Autowired
        private UserDetailsService userDetailsService;
    
    
        @Bean
        public BCryptPasswordEncoder bCryptPasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        /**
         * 身份认证接口  从写了这个接口,这里最重要的就是WebSecurityConfigurerAdapter接口下的configure方法,这个方法就是我们要实现的认证逻辑。其实也可以不重写configure方法,
            他默认就会去容器里面找PasswordEncoder实现类来作为认证的时候 密码加密 和数据库比较,以及userDetailsService实现类。
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    		// 告诉SpringSecurity 我们要使用自己定义的userDetailsService来通过username来获取主体,并且使用了BCryptPasswordEncoder加密进行密码比较
            auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    大厂面试总被刨根问底?原来是性能优化不过关
    《Java Web程序设计——开发环境搭建》
    设立“丰收杯”建设吨粮田 国稻种芯-株洲:破解种粮世界性难题
    信息系统项目管理师第四版学习笔记——配置与变更管理
    CoreData + CloudKit 在初始化 Schema 时报错 A Core Data error occurred 的解决
    《HCIP-openEuler实验指导手册》1.3Apache动态功能模块加载卸载练习
    【Java刷题进阶】基础入门篇⑦
    网络安全相关
    upload-labs文件上传1-5关
    Java_Jdbc
  • 原文地址:https://blog.csdn.net/dongdongdongJL/article/details/127666658