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

相關文章
相關標籤/搜索