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;@EnableWebSecuritypublic 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;@Componentpublic 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;@Controllerpublic 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也是正常登出的,自個兒動手試一下吧!


分享到:


相關文章: