1. 程式人生 > >SpringBoot 2.0.5簡單整合Spring Security遇到的坑

SpringBoot 2.0.5簡單整合Spring Security遇到的坑

SpringBoot整合Security的部落格案例網上已經很多了,但個人覺得對於一個初次整合Security的同學來說,一個簡單的案例還是很有必要的。為此,上傳一個本人整合的案例,僅供大家參考,也為自己記錄一下,話不多說,表演開始。

版本介紹:SpringBoot 2.0.5,JDK 1.8

首先建立SpringBoot專案,能看到這裡的同學,相信這一步就不用多說了,可以使用Eclipse中的Spring外掛建立,簡單有省事。

引入依賴:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.peng.demo</groupId>
	<artifactId>SpringBoot-security</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>SpringBoot-security</name>
	<description>SpringBoot-security</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

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

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

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

建立認證類,用於認證使用者

public class UserDetailsServiceImpl implements UserDetailsService {
	
	@Reference
	private UserService userService;
	
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		//構建角色
		List<GrantedAuthority> authorities=new ArrayList<GrantedAuthority>();
		authorities.add(new SimpleGrantedAuthority("ADMIN"));
		//查詢使用者
		TbUser tbUser = userService.findOne(username);
		if (tbUser!=null) {
			if (tbUser.getStatus().equals("1")) {
				return new User(username,tbUser.getPassword(), authorities);
			}else{
				return null;
			}
		}else{
			return null;
		}
	}

}

注:本人在實現本案例的時候,SpringBoot已整合Dubbo,所以其中的UserService是操作使用者的service,讀者可自己實現,通過查詢資料庫,返回使用者物件。@Reference註解為Dubbo中註解,讀者可用@Autowired代替。loadUserByUsername方法中的引數為頁面表單中輸入的引數值,後臺拿到username,通過userService查詢資料庫,即可判斷使用者是否存在。返回User物件,即可為使用者授權,其中的authorities為使用者的角色列表,這個角色列表也可以從資料庫中查詢出來,具體咋實現,可根據業務進行表設計。

注:SprintBoot2.0.5整合Security時,登入密碼需要進行編碼,所以在授權時,從資料庫中查詢的登入密碼需要經過編碼的,如果不進行編碼,需要注入

@Autowired     private BCryptPasswordEncoder bCryptPasswordEncoder;

返回的User為,對登陸密碼進行編碼:

return new User(username,bCryptPasswordEncoder.encode(tbUser.getPassword()), authorities);

建立WebSecurityConfigurerAdapter的實現,配置Security

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		//配置使用者名稱及密碼及角色
		auth.userDetailsService(userDetailsServiceImpl()).passwordEncoder(bCryptPasswordEncoder());
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		//配置攔截規則----配置訪問所有地址均需要ADMIN許可權,使用記憶體使用者使用hasAnyRole驗證使用者角色,使用資料庫使用者,使用hasAnyAuthority驗證使用者許可權
		http.authorizeRequests().antMatchers("/**").hasAnyAuthority("ADMIN");
		
		//配置登入頁面及退出等相關頁面
		http.formLogin().loginPage("/login.html")//登入頁面
		.usernameParameter("username").passwordParameter("password")//配置使用者名稱密碼引數名稱
		.loginProcessingUrl("/login")//配置登入請求路徑
		.defaultSuccessUrl("/admin/index.html",true)//登入成功跳轉,並且始終跳轉到/admin/index.html
		.failureUrl("/login.html")//登入失敗跳轉
		.and().logout().logoutUrl("/logout")//退出登入訪問地址
		.logoutSuccessUrl("/login.html")//退出成功後訪問頁面
		.and().csrf().disable()//配置不進行csrf攔截
		.headers().frameOptions().sameOrigin();//配置可以載入框架頁面  如iframe
	
		//自動登入
		http.rememberMe()
		.tokenValiditySeconds(432000);//設定cookie儲存時間 單位秒
	}
	
	@Override
    public void configure(WebSecurity web) throws Exception {
        //解決靜態資源被攔截的問題
        web.ignoring().antMatchers("/*.html","/css/**","/img/**","/js/**");
    }
	
	
	@Bean
	public BCryptPasswordEncoder bCryptPasswordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	public UserDetailsServiceImpl userDetailsServiceImpl() {
		return new UserDetailsServiceImpl();
	}
	
}

注:網上看到很多部落格寫到該配置時都會在當前配置的類頭上在加一個@Configuration註解,其實是沒必要的,檢視@EnableWebSecurity註解原始碼,即可檢視到其實已經配置@Configuration註解啦。在當前配置類中使用@Bean註解向IOC容器中加入密碼加密元件及我們自己配置的使用者認證元件,重寫WebSecurityConfigurerAdapter的三個方法,配置自己的登入及攔截規則,具體解釋程式碼中已有註釋。

注:Security也可以配置記憶體中的使用者名稱及密碼,當使用記憶體使用者名稱及密碼時,配置攔截規則可以為:http.authorizeRequests().antMatchers("/**").hasRole("ADMIN");

但使用資料庫中的使用者名稱及密碼通過GrantedAuthority設定角色時,配置攔截規則為:

http.authorizeRequests().antMatchers("/**").hasAnyAuthority("ADMIN");

這是一個坑,使用反了就會一直提示沒有許可權

編寫登入頁面login.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form id="myForm" action="/login" method="post">
		<input name="username" type="text" placeholder="郵箱/使用者名稱/手機號"><br/>
		<input name="password" type="password" placeholder="請輸入密碼"><br/>
		<input name="remember-me" type="checkbox">自動登入<br/>
		<input type="submit" value="登陸">
	</form>
</body>
</html>

注:登入方式為post提交,提交地址為/login

編寫/admin/index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	登陸成功<a href="/logout">退出登陸</a>
</body>
</html>

到此,SpringBoot整合Security就完成了,水平有限,如有錯誤,希望留言修正。