springsecurity通俗易懂

注:本篇写作思维旨在引导初学security的码友,一步步的达到可以灵活使用的地步,建议各位读者阅读前,先把我的源代码下载下来,对着master分支看比较好理解,磨刀不误砍柴工,https://github.com/1074952839/spring-security-demo01.git

springsecurity的引入(springboot版本为:2.1.3)

我们先准备一下如下的代码:

  1. 新建springboot项目,命名为:springboot-security-oauth2(之所以这么命名,是为了后期扩展项目达到企业级开发的目的)

  2. 然后我们建一个controller包,并在其中建一个UserController,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * Created by xk on 2018/11/26
    */
    @RestController
    @RequestMapping("/user")
    public class UserController {
    @GetMapping
    public String get(){
    return "看到我了吗?";
    }
    }
  3. 然后我们启动服务,访问localhost:8080/user,看到如下效果:

    image-20190228165043925

  4. 那问题来了,我想让这个接口受到保护,也就是需要用户登录后,才能访问这个接口怎么办呢?那好办呀,我们引入springsecurity试试呀!继续往下看吧。

我们继续接着上面的例子往下走,我们在pom.xml里面加入如下2个依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

然后我们再重新启动项目,访问localhost:8090/user,看到如下效果:

image-20190228225525071

哎呀,居然直接跳到localhost:8090/login地址了,然后出现这个表单了,这是因为springsecurity的默认配置。

那我们怎么才能访问到localhost:8090/user接口呢?

直捣黄龙吧,security默认的用户名为:user,密码可以在项目启动时发现,如图:

image-20190228230017073

填写完成后,就可以访问到localhost:8090/user接口了。

那么问题来了,我想自己做个注册功能,然后用我自己注册的用户名和密码登录咋办呢?

  1. 我们在MySQL的security-demo库里建一个user表,表结构如下:

    image-20190301084751810

  2. UserController里新增insert方法:

    1
    2
    3
    4
    5
    6
    7
    @Autowired
    private UserRepository userRepository; //本文档使用的是spring-data-jpa做数据访问框架

    @GetMapping("/register")//这么写其实不符合restful规范,这里为了好写demo,暂且这么写。
    public ResponseEntity<ResponseData> insert(User user){
    return ResponseEntity.ok(new ResponseData(userRepository.save(user)));//ResponseData这个类是我自己写的类
    }
  1. 重新启动项目,我们发送localhost:8090/user/register?name=test&password=test请求来注册一个用户,我们发现这时,请求并没有到UserController里,而是直接给我们返回一个登陆页面了!!!

  2. 上面的问题,其实不难发现,因为我们引入了spring-security,我们前面已经说了,他会默认的对所有请求做拦截,需要登录才能请求相关接口,而实际情况下,我们不应该拦截用户注册的请求呀?所以继续看如下神操作:

    1. 我们新建一个config包,然后新建WebSecurityConfig类,内容如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      /**
      * Created by xk on 2018/11/26
      */
      @Component
      public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
      @Override
      protected void configure(HttpSecurity http) throws Exception {
      http.formLogin()
      .and()
      .authorizeRequests()
      .antMatchers("/user/register").permitAll()//这里我们允许/user/register请求可以直接访问,其它请求都需要认证。
      .anyRequest()
      .authenticated();
      }
    2. 然后我们继续发送localhost:8090/user/register?name=test&password=test请求来注册一个用户,可以发现浏览器给我们返回了用户的信息,并且数据库已经有记录了。

      image-20190301102411207

    3. 接下来,我们继续访问localhost:8090/user吧,结果又跳到了登陆页面,那我们拿test,test登陆试试,结果悲剧了。。。。。。。。

      image-20190301102900769

      咋还登陆不上去呢???

    4. 接下来,继续神操作,我们再config包里新建UserService类,内容如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      @Component
      public class UserService implements UserDetailsService {
      @Autowired
      private UserRepository userRepository;
      @Override
      public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
      top.xiekun.springbootsecurityoauth2.domain.User user = userRepository.findByName(name);
      if (user != null){
      return new User(user.getName(), user.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));//这段话,后面的分享会讲解的。
      }
      return null;
      }
      }
    5. 我们继续访问localhost:8090/user吧,结果又跳到了登陆页面,那我们拿test,test登陆试试,结果又悲剧了,还是登陆不上去呀!我们先看一下下图:

      image-20190301130503173

      image-20190301130714438

      image-20190301130606248

      其实通过调试我们发现,springsecurity在做密码比对时,会先查看这个项目有木有配置PasswordEncoder,若没有的话就会抛出一个上图所示异常,所以才登陆不成功。

    6. 了解了问题的原因,那接下来我们将代码进行如下修改:

      1. WebSecurityConfig类添加如下内容:

        1
        2
        3
        4
        @Bean
        private PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
        }
      2. UserController类修改为如下内容:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        /**
        * Created by xk on 2018/11/26
        */
        @RestController
        @RequestMapping("/user")
        public class UserController {
        @Autowired
        private UserRepository userRepository;

        @Autowired
        private PasswordEncoder passwordEncoder;

        @GetMapping
        public List<User> get(){
        return userRepository.findAll();
        }

        @GetMapping("/register")
        public ResponseEntity<ResponseData> insert(User user){
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return ResponseEntity.ok(new ResponseData(userRepository.save(user)));
        }
        }
      3. 接下来我们发送localhost:8090/user/register?name=xiekun&password=123重新注册个用户,可以看到数据库多了一条记录,我们用新注册的用户名,密码发现登陆可以成功了。此时访问localhost:8090/user,发现可以成功查询到所有的用户了。

        image-20190301132309392

至此,我们已经完成了从用户注册到登陆成功后可以访问其他接口的目的了。(实际项目中,我们不可能说只要用户登录了,就可以请求各种接口了,我们应该给每个用户设置角色和权限,每个接口上面也应该注明需要用户有哪种角色和权限才能请求这个接口,那么接下来我会继续更新security+oauth2在项目中如何使用的。)

Xie Kun wechat
觉得不错,请喝奶茶吧😃