快速开始

快速开始

在 Spring Boot 项目中引入 Spring Security 同样是简单的引入 starter 封装:

// gradle
compile("org.springframework.boot:spring-boot-starter-security")

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

然后声明 Web Security 相关的配置:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ...
    }
    ...
}

UserInfo & UserInfoService

UserInfo 对应数据库表中的基本用户信息,如下:

public class UserInfo {

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 角色列表
     */
    private List<String> roles;

    //Getter & Settter
}

UserInfoService 模拟数据库查询,这里做了简化。

@Service
public class UserInfoService {

    /**
     * 查询用户信息
     * 1. 移除数据库交互,简单实现。
     * @param username 用户名称
     * @return 结果
     */
    public UserInfo queryUserInfo(final String username) {
        UserInfo userInfo = new UserInfo();
        if("user".equals(username) || "admin".equals(username)) {
            userInfo.setUsername(username);
            // 密码可以在入库的时候就进行加密
            userInfo.setPassword("123456");
            // 角色需要以 ROLE_ 开头
            userInfo.setRoles(Arrays.asList("ROLE_" + username));
            return userInfo;
        }

        throw new UsernameNotFoundException(username+"对应信息不存在");
    }

}

ROLE_ 这个前缀主要是为了后面使用角色注解授权的时候需要,默认的前缀就是这个。

WebSecurityConfig 核心配置类

@EnableWebSecurity 启用 web 安全,@EnableGlobalMethodSecurity(prePostEnabled = true) 启用方法级别的安全校验。

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级安全验证
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailsService myUserDetailsService;

    @Autowired
    private MyPasswordEncoder myPasswordEncoder;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService)
            .passwordEncoder(myPasswordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated() // 所有请求都需要验证
                .and()
                .formLogin().permitAll() // 使用默认的登录页面,登录页面允许所有用户访问
                .and()
                .csrf().disable();// post请求要关闭csrf验证,不然访问报错;实际开发中开启,需要前端配合传递其他参数
    }

}

一般情况下,MyUserDetailsService 和 MyPasswordEncoder 这两个类都是需要我们自定义的。

MyUserDetailsService 用户信息查询

我们只需要实现 UserDetailsService 接口,就可以实现对应的查询实现。这里的授权信息,直接使用 SimpleGrantedAuthority 类。

@Service
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UserInfoService userInfoService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserInfo userInfo = userInfoService.queryUserInfo(username);

        // 授权信息构建
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String role : userInfo.getRoles()) {
            authorities.add(new SimpleGrantedAuthority(role));
        }

        return new User(
                userInfo.getUsername(),
                userInfo.getPassword(),
                authorities
        );
    }
}

MyPasswordEncoder 密码加密策略

Spring Security 有很多内置的加密策略,这里为了演示,我自定义了最简单的 plainText 的策略,就是不做任何加密:

@Service
public class MyPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence rawPassword) {
        return (String) rawPassword;
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return rawPassword.equals(encodedPassword);
    }
}

AuthController 控制器

@RestController
public class AuthController {

    /**
     * 查看登录用户信息
     */
    @GetMapping("/auth")
    public Authentication auth(){
        return SecurityContextHolder.getContext().getAuthentication();
    }

    /**
     * 只能 user 角色才能访问该方法
     * @return 结果
     */
    @PreAuthorize("hasAnyRole('user')")
    @GetMapping("/user")
    public String user(){
        return "user角色访问";
    }

    /**
     * 只能 admin 角色才能访问该方法
     * @return 结果
     */
    @PreAuthorize("hasAnyRole('admin')")
    @GetMapping("/admin")
    public String admin(){
        return "admin角色访问";
    }

}

这里我们定义了 3 个方法,第一个方法是获取当前用户的登录信息。后面两个方法都是通过 @PreAuthorize 指定访问需要的角色信息。