简介可以参考百度百科,简单来说是一个优秀的web安全框架。
我们使用IDE新建一个maven工程,引入web 和security依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

Spring-security入门_spring security
我们编写一个控制器,不需要增加任何配置,springboot就完美整合了spring security。
Spring-security入门_编程_02

我们浏览器访问http://localhost:8080/test/hello,调到了登录页面,密码可以从控制台中获取到。
Spring-security入门_spring security_03

Spring-security入门_编程_04
默认用户名为user,密码每次控制台生成不一样的。

配置固定的用户名和密码
在application.properties文件中进行配置

server.port=8080
spring.security.user.name=huangbaokang
spring.security.user.password=123456

第二种方式,使用配置类来配置(之前配置文件的配置需要注释或者删除)

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123456");
        auth.inMemoryAuthentication().withUser("zhanglulu").password(password).roles("admin");
    }
}

启动,由于增加了@Configuration,故项目启动的时候会执行配置。但是访问的时候报
Spring-security入门_编程_05
需要增加一个PasswordEncoder的配置Bean

  @Bean
    public PasswordEncoder getPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

这样登录的账号密码变成了zhanglulu 密码123456

第三种方式,自定义配置
Spring-security入门_编程_06

我们需要了解UserDetailsService接口
我们编写一个自定义的实现UserDetailsService的实现类
Spring-security入门_编程_07
返回值是UserDetails接口,我们看下它的实现类
Spring-security入门_编程_08

我们可以用User

@Service("myUserDetailsService")
// 名字跟下面引入要一致
public class MyUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
        if(!username.equals("huangbaokang")){
            throw new UsernameNotFoundException("用户名不存在");
        }
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123456");
        return new User(username,password,auths);
    }
}
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private MyUserDetailsService myUserDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

       /* 第二种方式
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123456");
        auth.inMemoryAuthentication().withUser("zhanglulu").password(password).roles("admin");*/
       /*第三种方式,使用UserDetailsService*/
       auth.userDetailsService(myUserDetailsService).passwordEncoder(getPasswordEncoder());
    }

    @Bean
    public PasswordEncoder getPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

扩展下,如果使用数据库验证,我们则可以在自定义的UserDetailsService的loadUserByUsername方法内进行查询。
建表语句

create table users(
	id bigint primary key auto_increment,
	username varchar(20) unique not null,
	password varchar(100)
);

我们可以使用mybatis plus进行数据库的操作(当然任何一款都可以)。
引入依赖

<!--mybatis-plus-->
 <dependency>
	 <groupId>com.baomidou</groupId>
	 <artifactId>mybatis-plus-boot-starter</artifactId>
	 <version>3.0.5</version>
 </dependency>
 <!--mysql-->
 <dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.47</version>
</dependency>
 <!--lombok 用来简化实体类-->
 <dependency>
	 <groupId>org.projectlombok</groupId>
	 <artifactId>lombok</artifactId>
 </dependency>

这里mysql驱动我制定了版本,如果不指定,默认springboot2.4引入的mysql8,而我的mysql版本是5.7(虚拟机docker安装的)。
引入lombok,编写实体类(编辑器要安装该插件)

@Data
public class Users {
    private Integer id;
    private String username;
    private String password;
}

编写Mapper接口,我们继承BaseMapper

@Repository
public interface UsersMapper extends BaseMapper<Users> {
}

在配置文件application.properties中进行数据源配置

#mysql 数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver 
spring.datasource.url=jdbc:mysql://192.168.37.100:3306/demo
spring.datasource.username=root 
spring.datasource.password=123456

为了测试数据,我们需要知道123456的密文,编写一个测试类就可以获取到。
Spring-security入门_编程_09
插入到数据库中
Spring-security入门_编程_10
编写MyUserDetailsService的查询逻辑

@Service("myUserDetailsService")
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UsersMapper usersMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 使用mybatis-plus查询数据库
        QueryWrapper<Users> wrapper = new QueryWrapper<>();
        wrapper.eq("username",username);
        Users users = usersMapper.selectOne(wrapper);
        if(users == null){
            throw new UsernameNotFoundException("用户不存在");
        }
        // 设置角色
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
        return new User(users.getUsername(),users.getPassword(),auths);
    }
}

在启动类上增加mybatis的包扫描注解(一定要加)

@SpringBootApplication
@MapperScan("com.hbk.springsecurity.mapper")
public class SpringsecurityApplication {

这样我们就完成了一个查询数据库验证的案例。

修改默认的登录页面

默认的登录页面长这样,我们可以进行修改
Spring-security入门_spring security_11
我们可以在自定义的配置类中重写protected void configure(HttpSecurity http) throws Exception

package com.hbk.springsecurity.config;

import com.hbk.springsecurity.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private MyUserDetailsService myUserDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

       /* 第二种方式
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123456");
        auth.inMemoryAuthentication().withUser("zhanglulu").password(password).roles("admin");*/
       /*第三种方式,使用UserDetailsService*/
       auth.userDetailsService(myUserDetailsService).passwordEncoder(getPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login.html") // 设置登录页面地址
                .loginProcessingUrl("/test/login") // 设置登录页处理地址form 表单的action地址,路径随意,这个配置配合上面的登录地址
               // .successForwardUrl("/success.html") // 设置成功跳转地址
               // .failureUrl("/error.html") // 设置失败跳转地址
                .defaultSuccessUrl("/test/default")
               // .usernameParameter("uname") // 表单不是username
              //  .passwordParameter("pword") // 表单不是password
                .permitAll() // 设置默认成功跳转页面
                .and().authorizeRequests().antMatchers("/","/test/login").permitAll()// 设置不用认证的地址
                .anyRequest().authenticated() //其他请求需要认证授权
                .and().csrf().disable();// 关闭csrf防护
    }

    @Bean
    public PasswordEncoder getPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

新建login.html,注意name的取值为username,password
Spring-security入门_编程_12
增加默认成功跳转代码/test/default
Spring-security入门_spring security_13

访问控制器,将进入我们自定义的登录认证页面。
Spring-security入门_编程_14

源码解析
Spring-security入门_spring security_15
基于角色权限控制,主要学会如下几个方法即可

//.antMatchers("/test/index").hasAuthority("add")   
//.antMatchers("/test/index").hasAnyAuthority("add,delete")
//.antMatchers("/test/index").hasRole("sale")
 .antMatchers("/test/index").hasAnyRole("admin,sale")

注意设置角色时,底层源码增加了ROLE_
Spring-security入门_编程_16

自定义403错误页面

  // 自定义403错误页面
  http.exceptionHandling().accessDeniedPage("/error.html");

退出功能自定义跳转设置

http.logout().logoutUrl("/logout").logoutSuccessUrl("/index").permitAll
();

注解的使用

注解版的使用,先启用

@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled=true)

@Secured使用

@RequestMapping("testSecured")
@ResponseBody
@Secured({"ROLE_normal","ROLE_admin"})
public String helloUser() {
return "hello,user"; }

@PreAuthorize注解,进入方法前进行验证

@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequestMapping("/preAuthorize")
@ResponseBody
//@PreAuthorize("hasRole('ROLE_管理员')")
@PreAuthorize("hasAnyAuthority('menu:system')")
public String preAuthorize(){
 System.out.println("preAuthorize");
return "preAuthorize"; }

@PostAuthorize注解使用,方法执行后再进行权限验证,用的不多

@RequestMapping("/testPostAuthorize")
@ResponseBody
@PostAuthorize("hasAnyAuthority('menu:system')")
public String preAuthorize(){
 System.out.println("test--PostAuthorize");
return "PostAuthorize"; }

@PostFilter注解使用,权限验证之后数据过滤,如下,最终返回一条数据

@RequestMapping("getAll")
@PreAuthorize("hasRole('ROLE_管理员')")
@PostFilter("filterObject.username == 'admin1'")
@ResponseBody
public List<UserInfo> getAllUser(){
 ArrayList<UserInfo> list = new ArrayList<>();
 list.add(new UserInfo(1l,"admin1","6666"));
 list.add(new UserInfo(2l,"admin2","888"));
return list;
}

@PreFilter: 进入控制器之前对数据进行过滤

@RequestMapping("getTestPreFilter")
@PreAuthorize("hasRole('ROLE_管理员')")
@PreFilter(value = "filterObject.id%2==0")
@ResponseBody
public List<UserInfo> getTestPreFilter(@RequestBody List<UserInfo> 
list){
 list.forEach(t-> {
 System.out.println(t.getId()+"\t"+t.getUsername());
 });
return list;
}

后续我将编写一篇关于使用spring security 实现web工程的角色权限案例,带菜单的登录角色权限控制,结合数据库实现。