1.在pom.xml加入SpringSecurity的依赖

<!-- SpringSecurity对Web应用进行权限管理 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            </dependency>
        
        <!-- SpringSecurity配置 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            </dependency>
        
        <!-- SpringSecurity标签库 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            </dependency>

2.在web.xml加入springsecurity 的过滤器配置

<!-- springsecurity 的过滤器配置 -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

 

3.编写一个配置类WebAppSecurityConfig,该类要继承WebSecurityConfigurerAdapter

//表示当前类是一个配置类
@Configuration
//启用web环境下权限配置功能
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter{
    
}

4.在配置类中实现两个方法protected void configure(HttpSecurity security),configure(AuthenticationManagerBuilder builder)。

@Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
      // 装配userDetailsService对象
        builder
            .userDetailsService(userDetailsService).passwordEncoder(getBCryptPasswordEncoder());
        
    }
    
    @Override
    protected void configure(HttpSecurity security) throws Exception {
        
        // 准备JdbcTokenRepositoryImpl对象
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        
        // 创建数据库表
        tokenRepository.setCreateTableOnStartup(true);
        tokenRepository.initDao();
        
        security
            .authorizeRequests()            // 对请求进行授权
            .antMatchers("/index.jsp")        // 针对/index.jsp路径进行授权
            .permitAll()                    // 可以无条件访问
            .antMatchers("/layui/**")        // 针对/layui目录下所有资源进行授权
            .permitAll()                    // 可以无条件访问
            .antMatchers("/level1/**")        // 针对/level1/**路径设置访问要求
            .hasRole("学徒")                    // 要求用户具备“学徒”角色才可以访问
            .antMatchers("/level2/**")        // 针对/level2/**路径设置访问要求
            .hasAuthority("内门弟子")            // 要求用户具备“内门弟子”权限才可以访问
            .and()
            .authorizeRequests()            // 对请求进行授权
            .anyRequest()                    // 任意请求
            .authenticated()                // 需要登录以后才可以访问
            .and()
            .formLogin()                    // 使用表单形式登录
            
            // 关于loginPage()方法的特殊说明
            // 指定登录页的同时会影响到:“提交登录表单的地址”、“退出登录地址”、“登录失败地址”
            // /index.jsp GET - the login form 去登录页面
            // /index.jsp POST - process the credentials and if valid authenticate the user 提交登录表单
            // /index.jsp?error GET - redirect here for failed authentication attempts 登录失败
            // /index.jsp?logout GET - redirect here after successfully logging out 退出登录
            .loginPage("/index.jsp")        // 指定登录页面(如果没有指定会访问SpringSecurity自带的登录页)
            
            // loginProcessingUrl()方法指定了登录地址,就会覆盖loginPage()方法中设置的默认值/index.jsp POST
            .loginProcessingUrl("/do/login.html")    // 指定提交登录表单的地址
            .usernameParameter("loginAcct")            // 定制登录账号的请求参数名 默认为username
            .passwordParameter("userPswd")            // 定制登录密码的请求参数名 默认为password
            .defaultSuccessUrl("/main.html")        // 登录成功后前往的地址
            .and()
            .csrf()
            .disable()                                // 禁用CSRF功能
            .logout()                                // 开启退出功能
            .logoutUrl("/do/logout.html")            // 指定处理退出请求的URL地址
            .logoutSuccessUrl("/index.jsp")            // 退出成功后前往的地址
            .and()
            .exceptionHandling()                    // 指定异常处理器
            // .accessDeniedPage("/to/no/auth/page.html")    // 访问被拒绝时前往的页面
            .accessDeniedHandler(new AccessDeniedHandler() {
                
                @Override
                public void handle(HttpServletRequest request, HttpServletResponse response,
                        AccessDeniedException accessDeniedException) throws IOException, ServletException {
                    request.setAttribute("message", "抱歉!您无法访问这个资源!☆☆☆");
                    request.getRequestDispatcher("/WEB-INF/views/no_auth.jsp").forward(request, response);
                }
            })
            .and()
            .rememberMe()            // 开启记住我功能
            .tokenRepository(tokenRepository)    // 装配token仓库
            ;
    }

5.在启动时会报一个错误

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' available

原因:

三大组件启动顺序:

首先:ContextLoaderListener初始化,创建Spring的IOC容器

其次:DelegatingFilterProxy初始化,查找IOC容器,查找Bean

最后:DispatcherServlet初始化,创建SpringMVC的IOC容器

项目启动过程中,先进行ContextLoaderListener初始化加载spring配置文件生成springIOC容器,然后加载过滤器Filter,加载到DelegatingFilterProxy时需要在IOC容器中找一个SpringSecurityFilterChain的bean(默认会去Spring的IOC容器中去找),最后DispatcherServlet初始化加载SpringMvc配置文件生成SpringMVC的IOC容器。为了让SpringSecurity对浏览器请求进行权限控制,需要让SpringMVC来扫描WebAppSecurityConfig。所以DelegatingFilterProxy需要的bean在SpringMvc的IOC容器中。导致找不到这个Bean。

解决办法:

方法一.让SpringMVC的IOC容器和Spring的IOC容器合并,全部用DispatcherServlet来加载所有配置文件。

具体做法:

①在web.xml文件中注释掉监听器ContextLoaderListener的配置

②在springmvc配置文件中加载spring配置文件

<!-- 导入其他配置文件 -->
    <import resource="classpath:application*.xml"/>

方法二:修改源码

具体做法:

1.初始化时直接跳过查找IOC容器

①创建org.springframework.web.filter包,在这个包下创建类DelegatingFilterProxy,将源码拷贝过来。

按住Ctrl点击<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>,进入源码,拷贝出来。

<!-- springsecurity 的过滤器配置 -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

②去230行注释掉初始化查找IOC的代码

    // Fetch Spring root application context and initialize the delegate early,
    // if possible. If the root application context will be started after this
    // filter proxy, we'll have to resort to lazy initialization.
//                WebApplicationContext wac = findWebApplicationContext();
//                if (wac != null) {
//                    this.delegate = initDelegate(wac);
//                }

 

2.第一次加载时去找SpringMVC的IOC容器

①在DelegatingFilterProxy类251行左右进行如下修改

            //查找IOC容器,优先选择SpringIOC容器,注释掉,自己写
                    //WebApplicationContext wac = findWebApplicationContext();  
                    
                    
                    //1.获取ServletContext对象
                    ServletContext sc = this.getServletContext();
                    
                    //2.拼接SpringMVC将IOC容器存入ServletContext域的时候使用的属性名
                    String servletName = "springDispatcherServlet";
                    String attrName =FrameworkServlet.SERVLET_CONTEXT_PREFIX+servletName;
                    
                    //3.根据attrName从servletContext域中获取IOC容器
                    WebApplicationContext wac = (WebApplicationContext) sc.getAttribute(attrName);

最后启动服务就大功告成了!!!