一、簡介
Spring Security 提供了基於javaEE的企業應有個你軟件全面的安全服務。這里特別強調支持使用SPring框架構件的項目,Spring框架是企業軟件開發javaEE方案的領導者。如果你還沒有使用Spring來開發企業應用程序,我們熱忱的鼓勵你仔細的看一看。熟悉Spring特別是一來注入原理兩幫助你更快更方便的使用Spring Security。
人們使用Spring Secruity的原因有很多,單大部分都發現了javaEE的Servlet規范或EJB規范中的安全功能缺乏典型企業應用場景所需的深度。提到這些規范,重要的是要認識到他們在WAR或EAR級別無法移植。因此如果你更換服務器環境,這里有典型的大量工作去重新配置你的應用程序員安全到新的目標環境。使用Spring Security 解決了這些問題,也為你提供許多其他有用的,可定制的安全功能。
正如你可能知道的兩個應用程序的兩個主要區域是“認證”和“授權”(或者訪問控制)。這兩個主要區域是Spring Security 的兩個目標。“認證”,是建立一個他聲明的主題的過程(一個“主體”一般是指用戶,設備或一些可以在你的應用程序中執行動作的其他系統)。“授權”指確定一個主體是否允許在你的應用程序執行一個動作的過程。為了抵達需要授權的店,主體的身份已經有認證過程建立。這個概念是通用的而不只在Spring Security中。
二、實例
1、項目結構

2、applcaition配置文件
server:
port: 8088
servlet:
context-path: /springboot-security-demo
spring:
security:
user:
name: user
password: 123456
3、maven依賴
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>edu.hjl</groupId>
<artifactId>springboot-security-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-security-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--devtools熱部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope>
</dependency>
<!-- springboot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4、繼承WebSecurityConfigureAdapter配置角色權限
package edu.hjl.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());//passwoldEncoder是對密碼的加密處理,如果user中密碼沒有加密,則可以不加此方法。注意加密請使用security自帶的加密方式。
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//禁用了 csrf 功能
.authorizeRequests()//限定簽名成功的請求
.antMatchers("/decision/**","/govern/**","/employee/*").hasAnyRole("EMPLOYEE","ADMIN")//對decision和govern 下的接口 需要 EMPLOYEE 或者 ADMIN 權限
.antMatchers("/employee/login").permitAll()///employee/login 不限定
.antMatchers("/admin/**").hasRole("ADMIN")//對admin下的接口 需要ADMIN權限
.antMatchers("/oauth/**").permitAll()//不攔截 oauth 開放的資源
.anyRequest().permitAll()//其他沒有限定的請求,允許訪問
.and().anonymous()//對於沒有配置權限的其他請求允許匿名訪問
.and().formLogin()//使用 spring security 默認登錄頁面
.and().httpBasic();//啟用http 基礎驗證
}
}
上面設置了兩個角色:admin 和 employee;
admin可以訪問:"/decision/**","/govern/**","/employee/*","/admin/*";
employee可以訪問:"/descision/**","/govern/**","emplyoee/*";
從配置上看admin權限大於emplyoee。
5、SwaggerConfig配置類
package edu.hjl.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any()).build();
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("SpringBoot API Doc")
.description("This is a restful api document of Spring Boot.")
.version("1.0")
.build();
}
}
6、admin實體創建
package edu.hjl.pojo;
public class Admin {
private String username;
private String password;
public Admin() {
super();
}
public Admin(String username, String password) {
super();
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Admin [username=" + username + ", password=" + password + "]";
}
}
用於保存登陸用戶信息。
7、創建用戶繼承UserDetailService
package edu.hjl.service.impl;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import edu.hjl.pojo.Admin;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) {
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
Admin admin = new Admin();
if (username.equals("employee")) {
admin.setUsername("employee");
admin.setPassword("123456");
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_EMPLOYEE");
grantedAuthorities.add(grantedAuthority);
// 創建用戶,用戶判斷權限
return new User(
admin.getUsername(),
new BCryptPasswordEncoder().encode(admin.getPassword()),
grantedAuthorities);
}
if (username.equals("admin")) {
admin.setUsername("admin");
admin.setPassword("123456");
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_ADMIN");
grantedAuthorities.add(grantedAuthority);
// 創建用戶,用戶判斷權限
return new User(
admin.getUsername(),
new BCryptPasswordEncoder().encode(admin.getPassword()),
grantedAuthorities);
}
return null;
}
}
8、創建controller
admin controller:
package edu.hjl.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/admin/")
public class AdminController {
@RequestMapping(value = "greeting", method = RequestMethod.GET)
public String greeting() {
return "hello world";
}
@RequestMapping(value = "login", method = RequestMethod.GET)
public String login() {
return "login success";
}
}
emplyoee controller:
package edu.hjl.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/employee/")
public class EmployeeController {
@RequestMapping(value = "greeting", method = RequestMethod.GET)
public String greeting() {
return "hello world";
}
@RequestMapping(value = "login", method = RequestMethod.GET)
public String login() {
return "login success";
}
}
9、使用Swagger接口文檔進行測試
訪問地址:http://localhost:8088/springboot-security-demo/swagger-ui.html

點擊admin-controller

有兩個我們剛寫的方法,點擊greeting方法,調用的時候會彈出一個form

我們填入剛才創建的admin賬號密碼

返回狀態碼200,登陸成功,這是訪問login接口沒有權限限制問題。
除此之外,由於admin用戶擁有最多訪問權限,目前所有接口都可以訪問,不存在權限問題,接下來我們清除緩存,用emplyoee用戶登陸。
調用 /emplyoee/greeting接口

輸入登陸信息,登陸。

登陸成功,再次調用 /emplyoee/login接口,也同樣訪問成功。
接下來我們調用 /admin/greeting接口,看會發生什么?

狀態碼返回403,此次被我們對spring security截止了,禁止訪問,說明我們的配置起作用了。
三、總結
此次的案例,我后台是固定寫死的,真實開發中用戶權限的配置和訪問路徑是動態的,這個就需要按照各自的項目去配置。以上案例只是簡單的實現此功能。
源碼地址:https://github.com/JianLiangHe/springboot-security-demo
