Spring Security是Spring全家桶中的處理身份和權限問題的一員。Spring Security能夠根據使用者的須要定製相關的角色身份和身份所具備的權限,完成黑名單操做、攔截無權限的操做等等。java
本文將講解Springboot中使用spring security。git
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
複製代碼
因爲SpringBoot的自動配置的特性,引入了Spring Security依賴以後,已經默認幫咱們配置了。不信,如今訪問應用的根目錄:github
竟然跳到了一個登錄頁面,咱們什麼都沒有寫呀。咱們把spring security的依賴去掉,重啓應用,而後再次訪問根目錄:web
此次,熟悉的頁面出來了。其實這就是Springboot的魅力之處——自動配置。咱們只是引入了Spring Security的依賴,就自動幫咱們配置完了。spring
咱們能夠經過SecurityProperties的源碼查看,其默認帳號是user,密碼是啓動的時候隨機生成的,能夠在日誌裏找到:數據庫
咱們能夠經過配置文件修改Spring Security的默認帳號密碼,以下:json
spring.security.user.name=happyjava
spring.security.user.password=123456
複製代碼
springboot1.x版本爲:api
security.user.name=admin
security.user.password=admin
複製代碼
若是是一個普通的我的網站,如我的博客等。配置到這一步,已經能夠充當一個登錄控制模塊來使用了(固然還須要配置不須要登錄就能夠訪問的url)。springboot
雖然默認已經幫咱們實現了一個簡單的登錄認證模塊,可是在實際開發中,這仍是遠遠不夠的。好比,咱們有多個用戶,有多中角色等等。一切,仍是須要手動來開發。下面就一步一步來使用Spring Security:session
咱們固然須要配置不須要登錄就能訪問的路徑啦,好比:登錄接口(否則你怎麼訪問)。
有以下接口:
新建SecurityConfig,而後配置攔截的路徑,配置路徑白名單:
/** * @author Happy */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.antMatcher("/api/**")
.authorizeRequests()
.antMatchers("/api/notneedlogin/**").permitAll()
.anyRequest().authenticated();
}
}
複製代碼
排版亂請看圖片版
經過antMatchers("url").permitAll()方法,配置了/api/notneedlogin/**路徑會被Spring Security放行。
啓動項目驗證下:
須要登錄的接口攔截了返回403.
配置了白名單的路徑成功的獲取到了數據。
其實,這個時候已經能夠拿來當作一個普通我的網站的權限驗證模塊了,好比我的博客什麼的。
不少時候,咱們都須要自定義鑑權方式啦。好比,我不用session來鑑權了,改用無狀態的jwt方式(json web token)。這時候,咱們就要對Spring Security進行定製化了。
首先,咱們須要建立UserDetails的實現,這個就是Spring Security管理的用戶權限對象:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AdminUser implements UserDetails {
private String username;
@Override
@SuppressWarnings("unchecked")
public Collection<? extends GrantedAuthority> getAuthorities() {
// 這裏能夠定製化權限列表
return Collections.EMPTY_LIST;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
// 這裏設置帳號是否已通過期
return true;
}
@Override
public boolean isAccountNonLocked() {
// 這裏設置帳號是否已經被鎖定
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// 這裏設置憑證是否過時
return true;
}
@Override
public boolean isEnabled() {
// 是否可用
return true;
}
}
複製代碼
其次,咱們還須要一個過濾器,攔截請求判斷是否登錄,組裝UserDetails:
@Component
public class AuthFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String username = (String) request.getSession().getAttribute("username");
if (username != null && !"".equals(username)) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (userDetails != null && userDetails.isEnabled()) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails,
null, userDetails.getAuthorities());
// 把當前登錄用戶放到上下文中
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(
request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
} else {
// 用戶不合法,清除上下文
SecurityContextHolder.clearContext();
}
}
filterChain.doFilter(request, response);
}
}
複製代碼
這個過濾器裏的userDetailsService,是Spring Security加載UserDetails的一個接口,代碼以下:
它只有一個根據用戶名加載當前權限用戶的方法,個人實現以下:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// MOCK 模擬從數據庫 根據用戶名查詢用戶
AdminUserEntity adminUser = new AdminUserEntity(1, "happyjava", "123456");
// 構建 UserDetails 的實現類 => AdminUser
return new AdminUser(adminUser.getUsername());
}
}
複製代碼
MOCK這裏,須要用戶真正的去實現,我這裏只是演示使用。其中AdminUserEntity代碼以下:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdminUserEntity {
private Integer id;
private String username;
private String password;
}
複製代碼
到這裏,已經完成了Spring Security的整套配置了。
下面經過三個接口,測試配置是否生效:
增長了一個登錄接口,模擬真實用戶登錄。其中,needLogin接口,使用了AuthenticationPrincipal註解來獲取Spring Security中上下文的用戶(這個實在Filter裏面設置的)。
未登錄狀態,訪問test1接口:
直接被攔截掉了,調用登陸接口:
再次訪問:
成功請求到了接口。
本文演示的是使用session來完成鑑權的。使用session來作登陸憑證,一個很大的痛點就是session共享問題。雖然springboot解決session共享就幾個配置的問題,但終究仍是得依賴別的服務,這是有狀態的。
如今流行一種使用加密token的驗證方式來鑑權,本人在項目中也是使用token的方式的(jjwt)。其主要作法就是,用戶調用登錄接口,返回一串加密字符串,這串字符串裏面包含用戶名(username)等信息。用戶後續的請求,把這個token帶過來,經過解密的方式驗證用戶是否擁有權限。
本文講解了使用Spring Security來作鑑權框架,Spring Security配置起來仍是挺繁瑣的,可是配置完成以後,後續的獲取上下文用戶註解什麼的,是真的方便。我把代碼放到GitHub上,方便你們下載直接複製使用,地址以下:github.com/Happy4Java/…