原创

Spring Security(1)——基于内存认证信息(附完整项目代码)

1. 快速体验Security框架

Springboot项目集成Spring Security安全框架,需要做很多的工作,其中最重要的一个工作是实现Security框架的UserDetail接口,将数据库中的用户信息(用户名、密码)和权限信息加载出来。意味着我们要做DAO层、Service层、建表等工作。

这些工作对于想快速体验Security集成效果的读者来说就显得非常繁重了,因为首先你得有个数据库。

现在,本项目基于内存认证信息的demo就供读者直接运行,查看登入效果。读者下载完整demo项目直接启动,浏览器访问:http://localhost:8080

读者会看到下面的页面内容。

alt

没有登入,则会先跳转到login页面。判断是否满足权限,再决定页面跳转,

2. 配置pom依赖

   <dependencies>
    <!-- springboot依赖,springboot项目都需要配置  -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- spring security依赖,springBoot集成security框架pom依赖  -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- 下面两个依赖  是thymleaf模板依赖,本项目页面模板 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        <scope></scope>
    </dependency>
  </dependencies>

3. 基于内存认证的UserDetail

两种方式:用户信息放入内存

第一种

@EnableWebSecurity注解启动Spring Securiy,并使用Security框架自带的UserDetail实现类:org.springframework.security.provisioning.InMemoryUserDetailsManager。

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        //直接建两个用户存在内存中,生产环境可以从数据库中读取,对应管理器JdbcUserDetailsManager
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        // 创建两个用户
        //通过密码的前缀区分编码方式,推荐,这种加密方式很好的利用了委托者模式,使得程序可以使用多种加密方式,并且会自动
        //根据前缀找到对应的密码编译器处理。
        manager.createUser(User.withUsername("guest").password("{bcrypt}" +
                new BCryptPasswordEncoder().encode("111111")).roles("USER").build());
        manager.createUser(User.withUsername("root").password("{sha256}" +
                new StandardPasswordEncoder().encode("666666"))
                .roles("ADMIN", "USER").build());
        return manager;
    }

......
}

第二种

基于内存认证,还有一种方式将用户信息、权限信息注入到security框架中:覆盖实现方法configure(AuthenticationManagerBuilder auth),具体如下,这时候,覆盖方法userDetailsService()是是小的,即root、guest用户信息失效。

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // TODO Auto-generated method stub
//        super.configure(auth);
        //userdetail实现类bean也可以在这里注入到security中,
//        auth.userDetailsService(userDetailsService());
        //通过两种方式 用户认证信息放入到security中。
//        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("jack").password("{bcrypt}" +new BCryptPasswordEncoder().encode("88888888")).roles("USER");
//        auth.userDetailsService(userDetailsService());//覆盖了configure方法,那么覆盖userDetailsService()方法就失效了,因此root和guest都会失效,因此这里需要将UserDetail实现类bean注入到框架中。
        auth.inMemoryAuthentication().withUser(User.withUsername("jack").password("{bcrypt}" +new BCryptPasswordEncoder().encode("88888888")).roles("USER"));
    }

4. security配置

系统资源访问权限配置

antMatchers("/css/", "/js/", "/fonts/**").permitAll()
配置静态资源不用任何权限即可访问。

.antMatchers("/admin/**").hasAnyRole("ADMIN") // 满足该条件下的路由需要ROLE_ADMIN的角色
配置访问该path的资源需要用户认证且权限为“ADMIN”。

.anyRequest().authenticated()
其他未配置的资源访问都需要登入认证。

.formLogin().loginPage("/login").permitAll()
配置登入页面路径,且无需认证即可访问

logout().permitAll()
配置登出无需认证即可访问

exceptionHandling().accessDeniedHandler(accessDeniedHandler)
配置错误处理机制handler

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:on
        http.authorizeRequests()
                .antMatchers("/css/**", "/js/**", "/fonts/**").permitAll()  // 允许访问资源
                .antMatchers("/", "/home", "/about", "/login").permitAll() //允许访问这三个路由
                .antMatchers("/admin/**").hasAnyRole("ADMIN")   // 满足该条件下的路由需要ROLE_ADMIN的角色
                .antMatchers("/user/**").hasAnyRole("USER")     // 满足该条件下的路由需要ROLE_USER的角色
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll()
                .and()
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler).and()
                .csrf().disable();
        // @formatter:off
    }

5. 密码加密

两种配置加密方式

通过AuthenticationManagerBuilder指定和通过@Bean注入指定PasswordEncoder

auth.inMemoryAuthentication().withUser(User.withUsername("jack").password("{bcrypt}" +new BCryptPasswordEncoder().encode("88888888")).roles("USER"));

auth.inMemoryAuthentication().withUser(User.withUsername("king").password(passwordEncoder().encode("88888888")).roles("USER"));

对于两种方式,前者是将加密方式写死在编码逻辑中,而后者是通过一个抽象方法维护,这样的方式能通过模板方法模式更好地维护加密方式。

6. 登入逻辑测试

测试效果我就不展示了,大家可以clone demo项目运行后自行操作。先看看king用户登入的效果图。

访问Admin

alt

访问User

alt
完整的demo项目,请关注公众号“前沿科技bot“并发送"SEC-TWO"获取。

正文到此结束
本文目录