在上一篇文章中對Spring Boot 集成Shrio作了一個簡單的介紹,這篇文章中主要圍繞Spring Boot 集成 Spring Security展開,文章末尾附有學習資料。git
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
用戶權限功能的設計不是本篇文章的重點,這裏以一個簡單的例子做爲演示,須要建立兩個實體類一個枚舉類 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; }
首先須要從數據庫中查詢出來用戶數據交給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。學習
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(); } }
@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(); } }
咱們將用戶分紅了管理員與普通用戶,用戶頁面對用戶與管理可見,管理員頁面只對管理員可見
@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高。
這篇文章是匆忙中擠時間趕工出來的產物,有些地方也許寫的有些問題,歡迎提出反饋。下篇文章打算用以前所學的技術作一個簡單的項目,正在想作什麼,歡迎提出建議。