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

<!-- SpringSecurity對(duì)Web應(yīng)用進(jìn)行權(quán)限管理 -->
        <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標(biāo)簽庫(kù) -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            </dependency>

2.在web.xml加入springsecurity 的過(guò)濾器配置

<!-- springsecurity 的過(guò)濾器配置 -->
    <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.編寫一個(gè)配置類WebAppSecurityConfig,該類要繼承WebSecurityConfigurerAdapter

//表示當(dāng)前類是一個(gè)配置類
@Configuration
//啟用web環(huán)境下權(quán)限配置功能
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter{
    
}

4.在配置類中實(shí)現(xiàn)兩個(gè)方法protected void configure(HttpSecurity security),configure(AuthenticationManagerBuilder builder)。

@Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
      // 裝配userDetailsService對(duì)象
        builder
            .userDetailsService(userDetailsService).passwordEncoder(getBCryptPasswordEncoder());
        
    }
    
    @Override
    protected void configure(HttpSecurity security) throws Exception {
        
        // 準(zhǔn)備JdbcTokenRepositoryImpl對(duì)象
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        
        // 創(chuàng)建數(shù)據(jù)庫(kù)表
        tokenRepository.setCreateTableOnStartup(true);
        tokenRepository.initDao();
        
        security
            .authorizeRequests()            // 對(duì)請(qǐng)求進(jìn)行授權(quán)
            .antMatchers("/index.jsp")        // 針對(duì)/index.jsp路徑進(jìn)行授權(quán)
            .permitAll()                    // 可以無(wú)條件訪問(wèn)
            .antMatchers("/layui/**")        // 針對(duì)/layui目錄下所有資源進(jìn)行授權(quán)
            .permitAll()                    // 可以無(wú)條件訪問(wèn)
            .antMatchers("/level1/**")        // 針對(duì)/level1/**路徑設(shè)置訪問(wèn)要求
            .hasRole("學(xué)徒")                    // 要求用戶具備“學(xué)徒”角色才可以訪問(wèn)
            .antMatchers("/level2/**")        // 針對(duì)/level2/**路徑設(shè)置訪問(wèn)要求
            .hasAuthority("內(nèi)門弟子")            // 要求用戶具備“內(nèi)門弟子”權(quán)限才可以訪問(wèn)
            .and()
            .authorizeRequests()            // 對(duì)請(qǐng)求進(jìn)行授權(quán)
            .anyRequest()                    // 任意請(qǐng)求
            .authenticated()                // 需要登錄以后才可以訪問(wèn)
            .and()
            .formLogin()                    // 使用表單形式登錄
            
            // 關(guān)于loginPage()方法的特殊說(shuō)明
            // 指定登錄頁(yè)的同時(shí)會(huì)影響到:“提交登錄表單的地址”、“退出登錄地址”、“登錄失敗地址”
            // /index.jsp GET - the login form 去登錄頁(yè)面
            // /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")        // 指定登錄頁(yè)面(如果沒(méi)有指定會(huì)訪問(wèn)SpringSecurity自帶的登錄頁(yè))
            
            // loginProcessingUrl()方法指定了登錄地址,就會(huì)覆蓋loginPage()方法中設(shè)置的默認(rèn)值/index.jsp POST
            .loginProcessingUrl("/do/login.html")    // 指定提交登錄表單的地址
            .usernameParameter("loginAcct")            // 定制登錄賬號(hào)的請(qǐng)求參數(shù)名 默認(rèn)為username
            .passwordParameter("userPswd")            // 定制登錄密碼的請(qǐng)求參數(shù)名 默認(rèn)為password
            .defaultSuccessUrl("/main.html")        // 登錄成功后前往的地址
            .and()
            .csrf()
            .disable()                                // 禁用CSRF功能
            .logout()                                // 開(kāi)啟退出功能
            .logoutUrl("/do/logout.html")            // 指定處理退出請(qǐng)求的URL地址
            .logoutSuccessUrl("/index.jsp")            // 退出成功后前往的地址
            .and()
            .exceptionHandling()                    // 指定異常處理器
            // .accessDeniedPage("/to/no/auth/page.html")    // 訪問(wèn)被拒絕時(shí)前往的頁(yè)面
            .accessDeniedHandler(new AccessDeniedHandler() {
                
                @Override
                public void handle(HttpServletRequest request, HttpServletResponse response,
                        AccessDeniedException accessDeniedException) throws IOException, ServletException {
                    request.setAttribute("message", "抱歉!您無(wú)法訪問(wèn)這個(gè)資源!☆☆☆");
                    request.getRequestDispatcher("/WEB-INF/views/no_auth.jsp").forward(request, response);
                }
            })
            .and()
            .rememberMe()            // 開(kāi)啟記住我功能
            .tokenRepository(tokenRepository)    // 裝配token倉(cāng)庫(kù)
            ;
    }

5.在啟動(dòng)時(shí)會(huì)報(bào)一個(gè)錯(cuò)誤

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

原因:

三大組件啟動(dòng)順序:

首先:ContextLoaderListener初始化,創(chuàng)建Spring的IOC容器

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

最后:DispatcherServlet初始化,創(chuàng)建SpringMVC的IOC容器

項(xiàng)目啟動(dòng)過(guò)程中,先進(jìn)行ContextLoaderListener初始化加載spring配置文件生成springIOC容器,然后加載過(guò)濾器Filter,加載到DelegatingFilterProxy時(shí)需要在IOC容器中找一個(gè)SpringSecurityFilterChain的bean(默認(rèn)會(huì)去Spring的IOC容器中去找),最后DispatcherServlet初始化加載SpringMvc配置文件生成SpringMVC的IOC容器。為了讓SpringSecurity對(duì)瀏覽器請(qǐng)求進(jìn)行權(quán)限控制,需要讓SpringMVC來(lái)掃描WebAppSecurityConfig。所以DelegatingFilterProxy需要的bean在SpringMvc的IOC容器中。導(dǎo)致找不到這個(gè)Bean。

解決辦法:

方法一.讓SpringMVC的IOC容器和Spring的IOC容器合并,全部用DispatcherServlet來(lái)加載所有配置文件。

具體做法:

①在web.xml文件中注釋掉監(jiān)聽(tīng)器ContextLoaderListener的配置

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

<!-- 導(dǎo)入其他配置文件 -->
    <import resource="classpath:application*.xml"/>

方法二:修改源碼

具體做法:

1.初始化時(shí)直接跳過(guò)查找IOC容器

①創(chuàng)建org.springframework.web.filter包,在這個(gè)包下創(chuàng)建類DelegatingFilterProxy,將源碼拷貝過(guò)來(lái)。

按住Ctrl點(diǎn)擊<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>,進(jìn)入源碼,拷貝出來(lái)。

<!-- springsecurity 的過(guò)濾器配置 -->
    <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.第一次加載時(shí)去找SpringMVC的IOC容器

①在DelegatingFilterProxy類251行左右進(jìn)行如下修改

            //查找IOC容器,優(yōu)先選擇SpringIOC容器,注釋掉,自己寫
                    //WebApplicationContext wac = findWebApplicationContext();  
                    
                    
                    //1.獲取ServletContext對(duì)象
                    ServletContext sc = this.getServletContext();
                    
                    //2.拼接SpringMVC將IOC容器存入ServletContext域的時(shí)候使用的屬性名
                    String servletName = "springDispatcherServlet";
                    String attrName =FrameworkServlet.SERVLET_CONTEXT_PREFIX+servletName;
                    
                    //3.根據(jù)attrName從servletContext域中獲取IOC容器
                    WebApplicationContext wac = (WebApplicationContext) sc.getAttribute(attrName);

最后啟動(dòng)服務(wù)就大功告成了!!!