5

导读

在上一篇文章中对Spring Boot 集成Shrio做了一个简单的介绍,这篇文章中主要围绕Spring Boot 集成 Spring Security展开,文章末尾附有学习资料。

快速上手:

1.引入pom依赖

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

2.实现一个简单的用户权限类

用户权限功能的设计不是本篇文章的重点,这里以一个简单的例子作为演示,需要创建两个实体类一个枚举类

用户类:

@JsonIgnoreProperties(value = { "hibernateLazyInitializer","password" ,"new"})
@DynamicUpdate
@Entity
public class User extends AbstractPersistable<Long> {

    private static final long serialVersionUID = 2080627010755280022L;

    private String userName;

    @Column(unique = true, updatable = false)
    private String loginName;


    private String password;
    

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles;

    /**省略get/set**/
}

角色类别:

public enum RoleType {

    //級別从高到低 ADMIN->USER
    ADMIN,//管理员 

    USER//普通用户
}

角色类:

@Entity
public class Role extends AbstractPersistable<Long> {

    private static final long serialVersionUID = -856234002396786101L;

    @Enumerated(EnumType.STRING)
    @Column(name = "role_name", unique = true)
    private RoleType roleType;
}

3.定制自己的配置

首先需要从数据库中查询出来用户数据交给Spring Security这里有两种主要的方式:

AuthenticationProvider&&UserDetailsService两种方式的介绍:


Spring Security认证是由 AuthenticationManager 来管理的,但是真正进行认证的是 AuthenticationManager 中定义的 AuthenticationProvider。AuthenticationManager 中可以定义有多个 AuthenticationProvider。当我们使用 authentication-provider 元素来定义一个 AuthenticationProvider 时,如果没有指定对应关联的 AuthenticationProvider 对象,Spring Security 默认会使用 DaoAuthenticationProvider。DaoAuthenticationProvider 在进行认证的时候需要一个 UserDetailsService 来获取用户的信息 UserDetails,其中包括用户名、密码和所拥有的权限等。所以如果我们需要改变认证的方式,我们可以实现自己的 AuthenticationProvider;如果需要改变认证的用户信息来源,我们可以实现 UserDetailsService。

a.实现UserDetailsService 接口

public class CustomUserDetailsService implements UserDetailsService {

    @Autowired UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByLoginName(username);
        if(user == null){
            throw new UsernameNotFoundException("not found");
        }
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority(user.getRole().name()));
        System.err.println("username is " + username + ", " + user.getRole().name());
        return new org.springframework.security.core.userdetails.User(user.getUsername(),
                user.getPassword(), authorities);
    }

}

将自己的配置托管给Sprng 管理,Security为我们提供了WebSecurityConfigurerAdapter 我们只需要根据自己的需要进行继承重写即可

@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        return new CustomUserDetailsService();
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/index").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/admin")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

}

b.实现 AuthenticationProvider接口

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    private static final Logger logger = LoggerFactory.getLogger(CustomAuthenticationProvider.class);
   

    @Autowired
    private UserRepository userRepository;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String loginName = authentication.getName();
        String password = authentication.getCredentials().toString();
        List<GrantedAuthority> grantedAuths = new ArrayList<>();
        if (vaildateUser(loginName, password, grantedAuths)) {
            Authentication auth = new UsernamePasswordAuthenticationToken(loginName, password, grantedAuths);
            return auth;
        } else {
            return null;
        }
    }

    public boolean vaildateUser(String loginName, String password, List<GrantedAuthority> grantedAuths) {
        User user = userRepository.findByLoginName(loginName);
        if (user == null || loginName == null || password == null) {
            return false;
        }
        if (user.getPassword().equals(SHA.getResult(password)) && user.getUserStatus().equals(UserStatus.NORMAL)) {
            Set<Role> roles = user.getRoles();
            if (roles.isEmpty()) {
                grantedAuths.add(new SimpleGrantedAuthority(RoleType.USER.name()));
            }
            for (Role role : roles) {
                grantedAuths.add(new SimpleGrantedAuthority(role.getRoleType().name()));
                logger.debug("username is " + loginName + ", " + role.getRoleType().name());
            }
            return true;
        }
        return false;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

将配置托管给Spring

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;
    

    @Override  
    protected void configure(AuthenticationManagerBuilder auth)  
            throws Exception {   
        auth.authenticationProvider(customAuthenticationProvider);
    }  

    @Override
    protected void configure(HttpSecurity http) throws Exception {
         http
            .authorizeRequests()
                .antMatchers("/", "/index").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/admin")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

}

4.添加角色验证

我们将用户分成了管理员与普通用户,用户页面对用户与管理可见,管理员页面只对管理员可见

@Controller
public class UserController {

    PreAuthorize("hasAnyAuthority('ADMIN','USER')")
    @GetMapping("/user")
    public String user(){
        return "user";
    }
    
    @PreAuthorize("hasAnyAuthority('ADMIN')")@
    @GetMapping("/admin")
    public String admin(){
        return "admin";
    }
}       

Spring Security虽然要比Apache Shiro功能强大,但作为Spring 自家的应用与Spring 整合确实非常简单,同样Spring Security 学习成本要比Apache Shiro高。

结语

这篇文章是匆忙中挤时间赶工出来的产物,有些地方也许写的有些问题,欢迎提出反馈。下篇文章打算用之前所学的技术做一个简单的项目,正在想做什么,欢迎提出建议。

学习资料:

Spring Security 中文参考手册
Spring Security系列博客


yangrd
1.3k 声望225 粉丝

代码改变世界,知行合一。