1. 程式人生 > >Spring Security入門例項

Spring Security入門例項

目錄

第一章 程式架構

1.1 appliacition.properties的編寫 

第二章 程式碼編寫

2.1 domain層的編寫

2.1.1 SysUser

2.1.2 SysRole

2.1.3 Msg

2.2 dao層的編寫

2.3 service層的編寫

2.4 config的編寫

2.4.1 SecurityConfiguration的編寫

2.4.2 WebMvcConfig的編寫

2.5 controller的編寫

2.6 temeplates的書寫

第三章 操作之前的準備

3.1 執行的時候可能出現的錯誤

3.1.1 There is no PasswordEncoder mapped for the id “null”

參考:There is no PasswordEncoder mapped for the id “null”

3.1.2 Encoded password does not look like BCrypt

3.2 資料庫的配置

第四章 具體的操作


前言:這個通過SpringSecurity實現了一個許可權控制訪問,擁有指定許可權的使用者才能訪問指定的網頁.

SpringBoot的版本為2.1.1,springSecurity的版本為5.1.2,前端用的是thymeleaf.

第一章 程式架構

config存放了配置檔案,controller就是傳統上的控制頁面跳轉的,domain裡面裝的是資料模型,dao就是SpringJPA那一套,service裡放了一個自己實現的UserDetailsService,test用來測試一些東西

1.1 appliacition.properties的編寫 

這是複製的以前的專案的一個properties,和此專案相關性不大,只是為了能用省事,可根據自己的情況來配置

spring.datasource.url=jdbc:mysql://localhost:3306/testSpringSecurity?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.thymeleaf.prefix=classpath:/templates/

spring.jpa.open-in-view=true

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto=update
spring.thymeleaf.cache=false

logging.level.org.springframework.data=DEBUG

server.servlet.session.timeout=3600
server.servlet.context-path=/SpringSecurity
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

 

第二章 程式碼編寫

2.1 domain層的編寫

SysUser與SysRole是多對多的關係

2.1.1 SysUser

實現UserDetails是因為需要一個實現此介面的類來提供許可權等資訊.

@Entity
public class SysUser implements UserDetails{
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Long id;
	@Column
	private String username;
	@Column
	private String password;
	@ManyToMany(cascade= {CascadeType.REFRESH},fetch=FetchType.EAGER)
	private List<SysRole>  roles;
	public SysUser() {
	}
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		System.out.println("進入了cellection");
		List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
		List<SysRole> roles=this.getRoles();
		for(SysRole role:roles)
			{
				auths.add(new SimpleGrantedAuthority(role.getName()));
			}
		return auths;
	}

	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}

	//省略get,set...
}

2.1.2 SysRole

@Entity
public class SysRole {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Long id;
	@Column
	private String name;
	//省略get,set...
	
	
}

2.1.3 Msg

這個domain主要是我們為了測試一下controller與themeleaf執行的正確性

public class Msg {
	private String title;
	private String content;
	private String etraInfo;
	public  Msg(String title,String conten,String etraInfo) {
		super();
		this.title=title;
		this.content = conten;
		this.etraInfo = etraInfo;
	}
    //省略get,set...	

}

2.2 dao層的編寫

只要能根據使用者名稱查詢使用者即可

public interface SysUserRepository extends JpaRepository<SysUser,Long>{
	SysUser findByUsername(String username);
}

2.3 service層的編寫

SpringSecurity將呼叫實現了UserDetailService的類返回User

@Component
public class myUserDetailService implements UserDetailsService {
	@Autowired
	SysUserRepository sysUserRepository;
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		SysUser user = sysUserRepository.findByUsername(username);
		if(user==null) {
			
			System.out.println("沒有查詢到使用者");
		}
		return user;
	}

}

2.4 config的編寫

2.4.1 SecurityConfiguration的編寫

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
    
    //這裡將我們剛才寫的myUserDetailsService,將呼叫它返回user
	@Bean
	protected UserDetailsService myUserDetailsService() {
		return new myUserDetailService();
	}
    //將加密器註冊為BEAN
	@Bean
	protected PasswordEncoder  PasswordEncoder() {
		return new BCryptPasswordEncoder();
	}
    //這是新版SpringSecurity的要求,要求必須指定加密器,不然會報null的錯誤,這裡我們表示將用
//BCryptPasswordEncoder()來加密解密密碼.
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(myUserDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
	}
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests() //給請求授權,定義哪些URL需要被保護、哪些不需要被保護
			.antMatchers("/test1/**").hasRole("ADMIN")
			.antMatchers("/test2/**").hasRole("USER")
			.antMatchers("/test3/**").hasRole("TEACHER")
			.anyRequest().authenticated() //任何沒有匹配上的其他的url請求,都需要使用者被驗證
			.and()
			.formLogin()   //定義登陸的一些事項
				.loginPage("/login") //  定義當需要使用者登入時候,轉到的登入頁面
				.failureUrl("/login?error")
				.permitAll()
			.and()
			.logout().permitAll();
	}
}

 

2.4.2 WebMvcConfig的編寫

這裡就配置了一個頁面跳轉,和再controller配置效果是一樣的.

@Configuration
public class WebMvcConfig implements WebMvcConfigurer{
	
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		WebMvcConfigurer.super.addViewControllers(registry);
		registry.addViewController("/login").setViewName("login");
	}

}

2.5 controller的編寫

這裡登陸成功會自動跳轉到主頁.

@Controller
public class HomeController {
	@RequestMapping("/")
	public String index(Model model) {
		Msg msg = new Msg("測試標題", "測試內容", "額外資訊,只對管理員顯示");
		model.addAttribute("msg", msg);
		return "home";
	}
	@RequestMapping("/test1")
	public String  test1(Model model) {
		Msg msg = new Msg("測試標題", "測試內容", "額外資訊,只對管理員顯示");
		model.addAttribute("msg", msg);
		return "test1";
	}
	
	@RequestMapping("/test2")
	public String  test2(Model model) {
		Msg msg = new Msg("測試標題", "測試內容", "額外資訊,只對管理員顯示");
		model.addAttribute("msg", msg);
		return "test2";
	}
	
	@RequestMapping("/test3")
	public String  test3(Model model) {
		Msg msg = new Msg("測試標題", "測試內容", "額外資訊,只對管理員顯示");
		model.addAttribute("msg", msg);
		return "test3";
	}
	
}

2.6 temeplates的書寫

home:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta content="text/html;charset=UTF-8">
<title sec:authentication="name"></title>
</head>
<body>
	<a th:href="@{/}">首頁</a>
	<h1 th:text="${msg.title}"></h1>
	<p th:text="${msg.content}"></p>
	<!--這裡本來是想測試下thymeleaf的許可權功能,但是沒成功,但這不影響我們測試頁面的訪問功能 -->
	<div sec:authorize="isAuthenticated()">
		<p th:text="${msg.etraInfo}"></p>
	</div>
	
	<span sec:authorize="hasRole('ROLE_ADMIN')">管理員</span>
	
	<div sec:authorize="hasRole('ROLE_USER')">
		<p >無更多資訊顯示</p>
	</div>
	<form th:action="@{/logout}" method="post">
		<input type="submit" value="登出"/>
	</form>
	
</body>
</html>

test1:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>這是test1</title>
</head>
<body>
	<p th:text="${msg.content}"></p>
</body>
</html>

test2:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>這是test2</title>
</head>
<body>
	<p th:text="${msg.content]"></p>
</body>
</html>

test3:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>這是test3</title>
</head>
<body>
	<p th:text="${msg.content}"></p>
</body>
</html>

login:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html;charset=UTF-8"/>
<title>登陸頁面</title>
</head>
<body>
<a th:href="@{/}">首頁</a>
<p th:if="${param.logout}">已成功登出</p>
<p th:if="${param.error}">有錯誤,請重試</p>
<form th:action="@{/login}" method="POST">
	<label>賬號</label>
	<input type="text" name="username">
	<label>密碼</label>
	<input type="password" name="password">
	<input type="submit" id="login" value="login"/>
</form>
</body>
</html>

 

第三章 操作之前的準備

3.1 執行的時候可能出現的錯誤

3.1.1 There is no PasswordEncoder mapped for the id “null”

參考:There is no PasswordEncoder mapped for the id “null”

就是沒用類似BCrypt的加密器,現在這個版本的必須要用這個加密,按上面的加個BCrypt的Bean就好了.

3.1.2 Encoded password does not look like BCrypt

參考:https://blog.csdn.net/zhuyongru/article/details/82108543

就是資料庫的密碼不是加密過的,因為我們設定了加密器,所以加密器要求我們資料的密碼是加密過的,具體的形式如下圖.

如何插入加密後的密碼?見下圖,將使用者的密碼取出並加密

@SpringBootTest
@RunWith(SpringRunner.class)
public class MyTest {
	
	@Autowired
	SysUserRepository sysUserRepository;
	@Test
	public void test1() {
		System.out.println("第一個測試");
		SysUser user = sysUserRepository.findByUsername("zhangchen");
		BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
		user.setPassword(passwordEncoder.encode(user.getPassword()));
		sysUserRepository.save(user);
	}

}

 

3.2 資料庫的配置

sys_role

注意,資料庫裡的許可權我們要加上ROLE_

sys_user 

sys_user_roles

第四章 具體的操作

開啟登陸頁面

此時我們發現無論訪問什麼url都會跳轉到登陸,於是我們輸入賬號密碼進行登陸,登陸後跳轉到首頁

我們嘗試訪問/test1

再嘗試訪問/test2,可以訪問被禁止了,因為沒有USER許可權

我們加上USER許可權,再重新登陸,再訪問test2,成功了