Spring Boot [集成-Spring Security]

导读

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

快速上手:

1.引入pom依赖

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

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

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

用户类:spring

@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//普通用户
}

角色类:app

@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这里有两种主要的方式:ide

AuthenticationProvider&&UserDetailsService两种方式的介绍:spring-boot


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 咱们只须要根据本身的须要进行继承重写便可fetch

@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);
    }
}

将配置托管给Springui

@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系列博客

相关文章
相关标签/搜索