1.在application.yml中添加起步依賴java
2.自定義安全類web
package com.example.demo.readinglist; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private ReaderRepository readerRepository; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/").access("hasRole('READER')") .antMatchers("/**").permitAll() .and() .formLogin() .loginPage("/login") .failureUrl("/login?error=true"); } @Override protected void configure( AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(new UserDetailsService() { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserDetails userDetails = readerRepository.findOne(username); if (userDetails != null) { return userDetails; } throw new UsernameNotFoundException("User '" + username + "' not found."); } }); } }
3.定義實體類spring
package com.example.demo.readinglist; import java.util.Arrays; import java.util.Collection; import javax.persistence.Entity; import javax.persistence.Id; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @Entity public class Reader implements UserDetails { private static final long serialVersionUID = 1L; @Id private String username; private String fullname; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getFullname() { return fullname; } public void setFullname(String fullname) { this.fullname = fullname; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return Arrays.asList(new SimpleGrantedAuthority("ROLE_READER")); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
4.定義實體對應的倉庫數據庫
package com.example.demo.readinglist; import org.springframework.data.jpa.repository.JpaRepository; public interface ReaderRepository extends JpaRepository<Reader, String> { }
5.自定義參數解析器安全
package com.example.demo.readinglist; import org.springframework.core.MethodParameter; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; @Component public class ReaderHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return Reader.class.isAssignableFrom(parameter.getParameterType()); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Authentication auth = (Authentication) webRequest.getUserPrincipal(); return auth != null && auth.getPrincipal() instanceof Reader ? auth.getPrincipal() : null; } }
從安全認證返回的結果中得到參數實體類,其中爲何能從安全認證的結果中獲得實體類,後面會詳細說明app
6.在主應用程序中添加視圖控制器和參數解析器ide
package com.example.demo; import java.util.List; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import com.example.demo.readinglist.ReaderHandlerMethodArgumentResolver; @SpringBootApplication public class ReadingListApplication extends WebMvcConfigurerAdapter { public static void main(String[] args) { SpringApplication.run(ReadingListApplication.class, args); } //添加視圖控制器 @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login"); } //添加自定義參數解析器 @Override public void addArgumentResolvers( List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new ReaderHandlerMethodArgumentResolver()); } }
大致流程:源碼分析
Spring Boot在啓動的時候會先掃描到自定義的安全配置類SecurityConfig,登錄時會根據輸入的用戶與密碼從嵌入式數據庫中查找對應的記錄,若是找到了則表示認證成功ui
詳情以下:this
1.Spring Boot啓動時掃描安全配置類SecurityConfig,並設置權限控制和認證策略
權限控制
認證策略,該方法會將匿名內部類注入到DaoAuthenticationProvider的userDetailsService
2.輸入用戶名和密碼,點擊登陸,Spring Boot 根據參數使用DaoAuthenticationProvider的retrieveUser方法獲得登陸用戶詳情
圈出來的就是關鍵的部分,這裏就是調用了匿名內部類中重寫發loadUserByUsername方法
最終調用的就是根據用戶名查找用戶詳情的代碼(若是對Spring 的 repository高級特性不懂的再去百度一下)
而後使用DaoAuthenticationProvider類的additionalAuthenticationChecks進行密碼的比較
認證經過建立一個UsernamePasswordAuthenticationToken,而且屬性principal爲一個UserDetails類型的Reader(屬性已經所有賦值),則密碼不對則拋出異常
若是應用的控制器須要使用上面獲得的Reader,那麼使用以下代碼便可
Spring 判斷是否是目標方法的參數是否是支持的參數類型(Reader.class),若是是,則從request中取登陸認證結果對象做爲參數傳給目標方法,Spring實際上就是在綁定參數時調用了ReaderHandlerMethodArgumentResolver.resolveArgument方法作到的