Spring Security簡介及SpringBoot整合Spring Security

從事WEB開發依賴,一半用的安全認證框架,最多就是有兩種:Apache Shrio和Spring Security,而Spring Security作為Spring全家桶中的一員,相對於Spring集成起來更具有優勢和更好的支持!


Spring Security簡介及SpringBoot整合Spring Security


Spring Security簡介及SpringBoot整合Spring Security

Spring Security是一個強大且可定製的身份驗證和訪問控制框架,完全基於Spring的應用程序標準,它能夠為基於Spring的企業應用系統提供安全訪問控制解決方案的安全框架,它提供了一組可以在Spring應用上下文中配置的bean,充分利用了Spring IoC和AOP功能。用過apache shrio安全框架的碼友們都知道,安全認證框架主要包含兩個操作:認證(Authentication)和授權(Authorization)。Spring Security基於上述的兩個操作,也是提供了多個模塊:

1、核心模塊(spring-security-core.jar):包含核心的驗證和訪問控制類和接口,遠程支持和基本的配置API。任何使用Spring Security的應用程序都需要這個模塊。支持獨立應用程序、遠程客戶端、服務層方法安全和JDBC用戶配置。

2、遠程調用(spring-security-remoting.jar):提供與Spring Remoting的集成,通常我們不需要這個模塊,除非你要使用Spring Remoting編寫遠程客戶端。

3、Web網頁(spring-security-web.jar):包含網站安全相關的基礎代碼,包括Spring security網頁驗證服務和基於URL的訪問控制。

4、配置(spring-security-config.jar):包含安全命令空間的解析代碼。如果你使用Spring Security XML命令空間進行配置你需要包含這個模塊。

5、LDAP(spring-security-ldap.jar):LDAP驗證和配置代碼,這個模塊可用於LDAP驗證和管理LDAP用戶實體。

6、ACL訪問控制(spring-security-acl.jar):ACL專門的領域對象的實現。用來在你的應用程序中對特定的領域對象實例應用安全性。

7、CAS(spring-security-cas.jar):Spring Security的CAS客戶端集成。如果你想用CAS的SSO服務器使用Spring Security網頁驗證需要該模塊。

8、OpenID(spring-security-openid.jar):OpenID 網頁驗證支持。使用外部的OpenID服務器驗證用戶。

9、Test(spring-security-test.jar):支持Spring security的測試。


Spring Security簡介及SpringBoot整合Spring Security

在SpringBoot中配置Spring security中非常簡單,在pom.xml文件中加入Spring security的依賴,由於要使用靜態模板的支持,所以把thymeleaf的依賴也給引入進來:


Spring Security簡介及SpringBoot整合Spring Security


需要創建一個自定義類繼承WebSecurityConfigurerAdapter:


Spring Security簡介及SpringBoot整合Spring Security


Spring Security簡介及SpringBoot整合Spring Security


WebSecurityConfigurerAdapter是Spring security為Web應用提供的一個適配器,實現了WebSecurityConfigurer接口,提供了兩個方法用於重寫,從而實現開發者需求的安全配置。

方法configure(HttpSecurity http)可以通過http.authorizeRequests()定義哪些URL需要保護、哪些URL不需要,通過formLogin()方法定義當前用戶登陸的時候,跳轉到的登陸頁面。

方法configure(AuthenticationManagerBuilder auth)用於創建用戶和用戶的角色。

該自定義類代碼如下:

<code>package com.datang.security;

import org.springframework.beans.factory.annotation.Autowired;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
public class MySecurityConfigurer extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("MySecurityConfigurer HttpSecurity 調用...");
http.authorizeRequests()
.antMatchers("/login").permitAll() //都可以訪問路徑/login
.antMatchers("/", "/home").hasRole("ROCKET") //角色ROCKET可訪問
.antMatchers("/admin/**").hasAnyRole("LAKER", "HEAT") //角色LAKER、HEAT可訪問
.anyRequest().authenticated() //其他URL需要認證
.and()
.formLogin().loginPage("/login") //登陸請求
.successHandler(new MyAuthenticationSuccessHandler()) //登陸成功後的行為
.usernameParameter("username").passwordParameter("password") //登陸時接收的參數
.and()
.logout().permitAll() //登出,都可以訪問
.and()
.exceptionHandling().accessDeniedPage("/accessDenied"); //異常處理
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("MySecurityConfigurer AuthenticationManagerBuilder 調用...");
auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())
.withUser("t-mac").password("rocket1").roles("ROCKET"); //創建麥迪
auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())
.withUser("james").password("laker23").roles("LAKER", "HEAT"); //創建詹姆斯
}

}
/<code>

上面代碼中,當登陸成功後會有一個處理,就是登陸成功後怎麼跳轉,也就是我們自己定義的handler:new MyAuthenticationSuccessHandler(),該類的代碼如下:

<code>package com.datang.security;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Component
public class MyAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

//負責所有重定向事務
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

@Override
protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
String targetUrl = directTargetUrl(authentication);
redirectStrategy.sendRedirect(request, response, targetUrl);
}

protected String directTargetUrl(Authentication authentication) {
String url = "";
Collection extends GrantedAuthority> authorities = authentication.getAuthorities();
List<string> roles = new ArrayList<>();
for (GrantedAuthority a: authorities) {
roles.add(a.getAuthority());
}
if (isAdmin(roles)) {
url = "/admin";
} else if (isUser(roles)) {

url = "/home";
} else {
url = "/accessDenied";
}
System.out.println("url = " + url);
return url;
}

private boolean isUser(List<string> roles) {
if (roles.contains("ROLE_ROCKET")) {
return true;
} else {
return false;
}
}

private boolean isAdmin(List<string> roles) {
if (roles.contains("ROLE_LAKER")) {
return true;
} else {
return false;
}
}

}
/<string>/<string>/<string>/<code>

該類繼承了SimpleUrlAuthenticationSuccessHandler,提供了handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication)方法用於處理登陸成功後的URL重定向,其中directTargetUrl()方法是要獲取當前登陸者的角色,然後根據角色重定向到指定的URL。

在我們定義的MySecurityConfigurer類中,configure(AuthenticationManagerBuilder auth)方法也出現了一個我們自定義的類:new MyPasswordEncoder(),該類的代碼是:

<code>

它實現了PasswordEncoder接口,並實現了接口的兩個方法:encode()和matches(),這是一個密碼編輯器。

好了,現在基礎工作以及準備的差不多了,現在就寫個controller類來測試一下,首先寫一個基礎的BaseController,該類中實現了獲取用戶名getUsername()和獲取角色getAuthority()的方法,代碼如下:

<code>package com.datang.controller;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.ArrayList;
import java.util.List;

public class BaseController {

protected String getUsername() {
String name = SecurityContextHolder.getContext().getAuthentication().getName();
return name;
}

protected String getAuthority() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
List<string> roles = new ArrayList<>();
for (GrantedAuthority a:authentication.getAuthorities()) {
roles.add(a.getAuthority());
}
return roles.toString();
}
}
/<string>/<code>

然後寫一個測試類MySecurityController,繼承BaseController,其中代碼如下:

<code>package com.datang.controller;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
public class MySecurityController extends BaseController{

@RequestMapping("/")
@ResponseBody
public String index() {
return "首頁";

}

@RequestMapping("/home")
@ResponseBody
public String home() {
String username = getUsername();
System.out.println("username = " + username);
return "主頁 - 用戶名:" + username;
}

@RequestMapping("/login")
public String login() {
return "login";
}

@RequestMapping("/admin")
@ResponseBody
public String admin() {
String username = getUsername();
String role = getAuthority();
return "管理頁 - 用戶名:" + username + " - 角色:" + role;
}

@RequestMapping("/nba")
@ResponseBody
public String nba() {
String username = getUsername();
String role = getAuthority();
return "NBA - 用戶名:" + username + " - 角色:" + role;
}

@RequestMapping("/accessDenied")
@ResponseBody
public String accessDenied() {
return "無權限";
}

@RequestMapping("/logout")
@ResponseBody
public String logout(HttpServletRequest request, HttpServletResponse response) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
new SecurityContextLogoutHandler().logout(request, response, authentication);
}
return "用戶登出";
}
}

/<code>

由於是簡單的測試,所以就湊合寫一個簡單的登陸頁面login.html,其他頁面就用返回字符串來代替!

login.html代碼如下:

<code>





<title>登錄/<title>




/<code>

然後整體代碼結構如下,紅色邊框外的類和包自行忽略呀:


Spring Security簡介及SpringBoot整合Spring Security

啟動這個demo:


Spring Security簡介及SpringBoot整合Spring Security

看了咱們定義的URL訪問規則和用戶創建生效了,下面可以訪問了,輸入URL:http://localhost:9092/nba或者其他的諸如http://localhost:9092/aaa,會被重定向到http://localhost:9092/login,因為沒有登陸:


Spring Security簡介及SpringBoot整合Spring Security

輸入用戶james登陸,會被重定向到/admin,輸入t-mac會被重定向到/home:


Spring Security簡介及SpringBoot整合Spring Security


Spring Security簡介及SpringBoot整合Spring Security

此時再訪問http://localhost:9092/nba就可以了:


Spring Security簡介及SpringBoot整合Spring Security

其他登出/logout也是正常登出的,自個兒動手試一下吧!


分享到:


相關文章: