zoukankan      html  css  js  c++  java
  • Spring in action读书笔记(七) Spring Security启用权限管理实践

    所需jar包

    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
    <servletApiVersion>3.1.0</servletApiVersion>
    <springSecurityVersion>3.2.0.RELEASE</springSecurityVersion>
    <springVersion>4.0.7.RELEASE</springVersion>
    <thymeleafVersion>2.1.3.RELEASE</thymeleafVersion>
    </properties>

    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${springVersion}</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${springVersion}</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>${springSecurityVersion}</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>${springSecurityVersion}</version>
    </dependency>


    <dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring4</artifactId>
    <version>${thymeleafVersion}</version>
    </dependency>
    <dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity3</artifactId>
    <version>2.1.1.RELEASE</version>
    </dependency>

    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>${servletApiVersion}</version>
    </dependency>
    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.9</version>
    </dependency>
    </dependencies>

    一  启用Spring Security最简单的配置

    1、MVC启用类,配置thymeleaf

    WebInitializer.java

    package test;
    
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    
    public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
      
      @Override
      protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };
      }
    
      @Override
      protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfig.class };
      }
    
      @Override
      protected String[] getServletMappings() {
        return new String[] { "/" };
      }
    }

    WebConfig.java

    package test;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    import org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect;
    import org.thymeleaf.spring4.SpringTemplateEngine;
    import org.thymeleaf.spring4.view.ThymeleafViewResolver;
    import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
    import org.thymeleaf.templateresolver.TemplateResolver;
    
    @Configuration
    @EnableWebMvc
    @ComponentScan("test")
    public class WebConfig extends WebMvcConfigurerAdapter {
    
      @Bean
      public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine);
        return viewResolver;
      }
    
      @Bean
      public SpringTemplateEngine templateEngine(TemplateResolver templateResolver) {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        templateEngine.addDialect(new SpringSecurityDialect());
        return templateEngine;
      }
    
      @Bean
      public TemplateResolver templateResolver() {
        TemplateResolver templateResolver = new ServletContextTemplateResolver();
        templateResolver.setPrefix("/WEB-INF/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode("HTML5");
        return templateResolver;
      }
    }

    RootConfig.java

    package test;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.ComponentScan.Filter;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    
    @Configuration
    @ComponentScan(basePackages={"test"},
        excludeFilters={
            @Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)
        })
    public class RootConfig {
    
    }

    2、过滤web请求,将web请求委托给Spring Security相关的Filter

    package test;
    
    import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
    
    public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
    }

    3、web应用启用Spring Security

    package test;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
    
    @Configuration
    @EnableWebMvcSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    
    }

    访问http://localhost:8080/会链接到http://localhost:8080/login,即Spring security提供的基本登录也功能。默认要求所有的http请求都要认证。

    相当于SecurityConfig类重写configure(HttpSecurity)方法

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                    .formLogin().and()
                    .httpBasic();
        }

     二  基于内存用户认证

    基于内存配置用户,SecurityConfig中重写configure(AuthenticationManagerBuilder)方法,并且配置除首页外其他路径都需要进行认证。

    package test;
    
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    import java.util.Objects;
    
    
    @Configuration
    @EnableWebMvcSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/home", "/").permitAll()
                    .anyRequest().authenticated()
                    .and()
                    //启用默认登录页面
                    .formLogin().and()
                    .httpBasic();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
            auth.inMemoryAuthentication()
                    //密码加密方法,这里简单地反转字符串, 设置的密码是加密后的密码, 因此可使用 user作为用户名、password作为密码进行登录
                    .passwordEncoder(new ReversePasswordEncoder())
                    .withUser("user").password(StringUtils.reverse("password")).authorities("ROLE_USER")
                    .and()
                    .withUser("admin").password(StringUtils.reverse("password")).authorities("ROLE_USER", "ROLE_ADMIN");
        }
    
        private static class ReversePasswordEncoder implements PasswordEncoder {
    
            @Override
            public String encode(CharSequence rawPassword) {
                return StringUtils.reverse(String.valueOf(rawPassword));
            }
    
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return Objects.equals(encode(rawPassword), encodedPassword);
            }
        }
    
    }

    配置controller,创建可访问的html页面

    package test;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import static org.springframework.web.bind.annotation.RequestMethod.GET;
    
    @Controller
    @RequestMapping("/")
    public class HomeController {
    
        @RequestMapping(method = GET, value = {"", "/home"})
        public String home(Model model) {
            return "home";
        }
    
        @RequestMapping(method = GET, value = "/authenticate")
        public String authenticate() {
            return "authenticate";
        }
    }

    在webapp/WEB-INF/templates目录(与WebConfig中TemplateResolver  bean相对应)下新建home.html、authenticate.html文件

    home.html

    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Home</title>
      </head>
      <body>
        Home Page
      </body>
    </html>

    authenticate.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <title>Title</title>
    </head>
    <body>
    Authenticated
    </body>
    </html>

    启动Spring应用, 不登录时可访问http://localhost:8080、http://localhost:8080/home,访问http://localhost:8080/authenticate 会跳转到登录页面,

    登录后访问 http://localhost:8080/authenticate (登录用户名为user、admin,密码为password。localhost:8080/logout 会退出登录)

     三  用户认证、权限管理及自定义用户服务(如从Redis等NoSql数据库中取用户数据)

    修改HomeController类,增加html页面,并创建admin.html文件

    package test;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import static org.springframework.web.bind.annotation.RequestMethod.GET;
    
    @Controller
    @RequestMapping("/")
    public class HomeController {
    
        @RequestMapping(method = GET, value = {"", "/home"})
        public String home(Model model) {
            return "home";
        }
    
        @RequestMapping(method = GET, value = "/authenticate")
        public String authenticate() {
            return "authenticate";
        }
    
        @RequestMapping(method = GET, value = "/admin")
        public String admin() {
            return "admin";
        }
    }

    admin.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <title>Admin</title>
    </head>
    <body>
    Admin
    </body>
    </html>

    修改SecurityConfig类,配置页面访问所需权限,首页home.html允许所有用户访问,admin.html允许ADMIN用户访问,其他页面允许认证用户访问

    package test;
    
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    import java.util.Objects;
    
    
    @Configuration
    @EnableWebMvcSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/home", "/").permitAll()
                    .antMatchers("/admin").hasAuthority("ROLE_ADMIN")
                    .anyRequest().authenticated()
                    .and()
                    //启用默认登录页面
                    .formLogin().and()
                    .httpBasic();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            //自定义的用户服务
            auth.userDetailsService(new UserDetailServiceImpl()).passwordEncoder(new ReversePasswordEncoder());
        }
    
        private static class ReversePasswordEncoder implements PasswordEncoder {
    
            @Override
            public String encode(CharSequence rawPassword) {
                return StringUtils.reverse(String.valueOf(rawPassword));
            }
    
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return Objects.equals(encode(rawPassword), encodedPassword);
            }
        }
    
    }

    其中UserDetailServiceImpl类如下,配置了两个用户数据(用户数据可从数据库中获取)

    package test;
    
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.security.core.GrantedAuthority;
    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 java.util.Arrays;
    
    public class UserDetailServiceImpl implements UserDetailsService {
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            if ("user".equals(username)) {
                return new User("user", StringUtils.reverse("password"), Arrays.asList(new Role("ROLE_USER")));
            } else if ("admin".equals(username)) {
                return new User("admin", StringUtils.reverse("password"), Arrays.asList(new Role("ROLE_USER"), new Role("ROLE_ADMIN")));
            }
    
            throw new UsernameNotFoundException(String.format("User '%s' no found", username));
        }
    
        private static class Role implements GrantedAuthority {
    
            private String role;
    
            private Role(String role) {
                this.role = role;
            }
    
            @Override
            public String getAuthority() {
                return String.valueOf(role);
            }
        }
    
    }

    启动应用程序,其中admin账号(密码password)可以访问 http://localhost:8080/admin、http://localhost:8080/authenticate、http://localhost:8080/home,

                            user账号(密码password)可以访问http://localhost:8080/authenticate、http://localhost:8080/home, 访问http://localhost:8080/admin 会报403错误。

                           不登录情况下只能访问http://localhost:8080/home

    四  自定义登录页面

    修改WebConfig类,增加/login对应的视图

    package test;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    import org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect;
    import org.thymeleaf.spring4.SpringTemplateEngine;
    import org.thymeleaf.spring4.view.ThymeleafViewResolver;
    import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
    import org.thymeleaf.templateresolver.TemplateResolver;
    
    @Configuration
    @EnableWebMvc
    @ComponentScan("test")
    public class WebConfig extends WebMvcConfigurerAdapter {
    
        @Bean
        public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
            ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
            viewResolver.setTemplateEngine(templateEngine);
            return viewResolver;
        }
    
        @Bean
        public SpringTemplateEngine templateEngine(TemplateResolver templateResolver) {
            SpringTemplateEngine templateEngine = new SpringTemplateEngine();
            templateEngine.setTemplateResolver(templateResolver);
            templateEngine.addDialect(new SpringSecurityDialect());
            return templateEngine;
        }
    
        @Bean
        public TemplateResolver templateResolver() {
            TemplateResolver templateResolver = new ServletContextTemplateResolver();
            templateResolver.setPrefix("/WEB-INF/templates/");
            templateResolver.setSuffix(".html");
            templateResolver.setTemplateMode("HTML5");
            return templateResolver;
        }
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/login").setViewName("login");
        }
    }

    新建thymeleaf模板HTML: home.html及page.html

    home.html

    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Test-login</title>
    </head>
    <body onload='document.f.username.focus();'>
    <div id="header" th:include="page :: header"></div>
    <div id="content">
        <form name='f' th:action='@{/login}' method='POST'>
            <table>
                <tr>
                    <td>User:</td>
                    <td>
                        <input type='text' name='username' value=''/></td>
                </tr>
                <tr>
                    <td>Password:</td>
                    <td><input type='password' name='password'/></td>
                </tr>
                <tr>
                    <td colspan='2'>
                        <input id="remember_me" name="remember-me" type="checkbox"/>
                        <label for="remember_me" class="inline">Remember me</label></td>
                </tr>
                <tr>
                    <td colspan='2'>
                        <input name="submit" type="submit" value="Login"/></td>
                </tr>
                <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
            </table>
        </form>
    </div>
    <div id="footer" th:include="page :: copy"></div>
    </body>
    </html>

    page.html

    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:th="http://www.thymeleaf.org">
          
      <body>
      
        <div th:fragment="header">
          <a th:href="@{/logout}">Logout</a>
        </div>
    
        <div>Content goes here</div>
      
        <div th:fragment="copy">Copyright &copy; Craig Walls</div>
      </body>
      
    </html>

    修改SecurityConfig类的configure(HttpSecurity)方法, 设置自定义的登录页面

    package test;
    
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl;
    
    import java.util.Objects;
    
    
    @Configuration
    @EnableWebMvcSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/home", "/", "/login").permitAll()
                    .antMatchers("/admin").hasAuthority("ROLE_ADMIN")
                    .anyRequest().authenticated()
                    .and()
                    //设置自定义登录页面
                    .formLogin().loginPage("/login").and()
                    //设置退出后重定向页面
                    .logout().logoutSuccessUrl("/").and()
                    //启用http basic认证
                    .httpBasic().and()
                    //启用remember me 功能,   会在浏览器的cookie中增加一个token, 该token包含用户名、密码、过期时间和一个私钥
                    .rememberMe()
                    .tokenRepository(new InMemoryTokenRepositoryImpl())
                    .tokenValiditySeconds(2419200)
                    .key("test");
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
            auth.userDetailsService(new UserDetailServiceImpl()).passwordEncoder(new ReversePasswordEncoder());
        }
    
        private static class ReversePasswordEncoder implements PasswordEncoder {
    
            @Override
            public String encode(CharSequence rawPassword) {
                return StringUtils.reverse(String.valueOf(rawPassword));
            }
    
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return Objects.equals(encode(rawPassword), encodedPassword);
            }
        }
    
    }

    重启程序,访问没有权限的页面时会跳转到自定义的登录页面

  • 相关阅读:
    oracle 增加列
    20120621 myeclipse 远程调试
    plsql 参数中in out in的区别讲解
    20120606 随笔
    MYSQL申明变量&显示变量
    arcgis for flex 地图发布服务
    arcserver 地图发布过程
    arcserver 发布地图后浏览器不更新问题
    把一个表的一列插入另一个表的空字段
    mysql运行语句时出现 FUNCTION *** does not exist
  • 原文地址:https://www.cnblogs.com/wushengwuxi/p/12122148.html
Copyright © 2011-2022 走看看