一、Spring Security是什么(百度百科)

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

二、初使用

Spring Security是自带登录页面的,所以我们可以直接使用它的页面,或者也可以自己编写页面。以下均使用框架自带的页面。

Spring Security 你的登录已在另一台机器登录 你一被迫下线 spring security登陆_User

1. 用户在代码中自定义(无数据库版)
1.1 在yaml文件直接配置用户
spring:
  security:
    user:
      name: user
      password: 123

在yaml文件中直接配置如上所示的代码后就可以直接使用user账号登录了。

1.2 在代码中配置用户

我们可以通过继承WebSecurityConfigurerAdapter类来配置我们的用户,我们只要重写里面configure方法就可以了。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启角色权限的注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.inMemoryAuthentication()
            .withUser("admin")//用户名
            .password(passwordEncoder().encode("123456"))//密码加密
            .roles("admin");//角色

        auth.inMemoryAuthentication()
                .withUser("user")//用户名
                .password(passwordEncoder().encode("123"))//密码加密
                .roles("normal");//角色

    }


    @Bean
    public PasswordEncoder passwordEncoder(){
        //将加密的类注册为bean
        return new BCryptPasswordEncoder();
    }
}

如上代码所示,你就注册了两个用户,admin和user。值得注意的是密码是要加密的,这里我选择了BCryptPasswordEncoder,并将其注册为bean。

还有这里的角色roles是指在控制器上的注解,如下图所示。

Spring Security 你的登录已在另一台机器登录 你一被迫下线 spring security登陆_List_02


在使用上图的方法的时候,记得一定要开启@EnableGlobalMethodSecurity这个角色的权限注解。控制器的代码我就附在下面了。

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }

    @GetMapping("/helloAdmin")
    @PreAuthorize("hasAnyRole('admin')")
    public String helhelloAdminlo() {
        return "hello,Admin";
    }


    @GetMapping("/helloNormal")
    @PreAuthorize("hasAnyRole('normal','admin')")
    public String helloNormal() {
        return "hello,Normal";
    }
}
2.连接数据库,通过数据库的用户来登录

使用数据的用户的话,我们可以继承UserDetailsService类,重写loadUserByUsername方法来实现。

package com.security.securitytest.config;

import com.security.securitytest.entity.Role;
import com.security.securitytest.entity.UserInfo;
import com.security.securitytest.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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 org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Component
public class MyUserDetailsService implements UserDetailsService {

    @Resource
    private UserService userServiceImpl;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("loadUserByUsername(String username)");
        System.out.println("username="+username);
        //这里通过自己写的服务层的方法获取用户信息
        UserInfo userInfo = userServiceImpl.findUserByUsername(username);
        if (userInfo == null){
            //如果没有该用户就抛出"not found"信息
            throw new UsernameNotFoundException("not found");
        }

        //创建一个权限的集合
        List<GrantedAuthority> authorities = new  ArrayList<GrantedAuthority>();

        //通过for循环获取用户里的角色信息
        for(Role role : userInfo.getRoles()){
            System.out.println("Role_"+role.getName());
            //这里要注意我们需要在获取的角色名前加上"ROLE_",并且注意ROLE四个字母一定都要大写
            authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
        }

        //创建用户信息,这里的user是引用org.springframework.security.core.userdetails.User类
        User user = new User(userInfo.getUsername(),passwordEncoder.encode(userInfo.getPassword()),authorities);

        return user;


    }
}

这里附带一下我的实体类和数据库的设计方便说明。
首选是实体类的UserInfo和Role

1.UserInfo类

public class UserInfo {

        private int id;

    private String username;

    private String password;

    //这里我直接在用户的实体类里创建一个角色的List,我就不建pojo了,主要是懒,MyBatis啥的关联查询就自己写一下吧,我就不给了,应该不会有人不会写吧
    private List<Role> roles;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
}

2.Role 类

public class Role {

    private int id;

    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

接着是数据设计的截图

1.user表

Spring Security 你的登录已在另一台机器登录 你一被迫下线 spring security登陆_List_03


2. role表

Spring Security 你的登录已在另一台机器登录 你一被迫下线 spring security登陆_spring_04


3. user_role(角色用户表)

Spring Security 你的登录已在另一台机器登录 你一被迫下线 spring security登陆_spring_05