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);
最后启动服务就大功告成了!!!