西寧企業(yè)做網(wǎng)站互聯(lián)網(wǎng)公司有哪些
前言
Spring Security 是一款強(qiáng)大且高度可定制的認(rèn)證和訪問控制框架,它是為了保護(hù)基于Spring的應(yīng)用程序提供安全性支持。Spring Security提供了全面的安全服務(wù),主要針對企業(yè)級應(yīng)用程序的需求。其核心組件主要包含:Authentication(認(rèn)證)、Authorization(授權(quán))、Principal(主體)、Granted Authority(授予的權(quán)限)、Security Context(安全上下文),可提供方法級別的權(quán)限認(rèn)證。其底層主要通過Filter攔截器實現(xiàn),關(guān)于其實現(xiàn)原理,我們會在后面的章節(jié)內(nèi)容中介紹。
本節(jié)內(nèi)容主要是關(guān)于前后端分離的項目如何集成spring securtity安全框架,完成用戶的認(rèn)證與授權(quán)。也會詳細(xì)介紹各個配置項及配置組件的使用。
正文
①創(chuàng)建一個springboot web項目,引入spring security的依賴,同時引入mysql和mybatis-plus依賴,用于實現(xiàn)一個數(shù)據(jù)庫版本的spring security安全框架
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.22</version>
</dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.4</version>
</dependency><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>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
?②在application.yml中添加數(shù)據(jù)庫配置,用于連接數(shù)據(jù)庫
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.110.88:3306/ht-atp?characterEncoding=utf-8&serverTimezone=GMT%2B8&useAffectedRows=true&nullCatalogMeansCurrent=trueusername: rootpassword: root
server:port: 8080
③ 創(chuàng)建一張用戶數(shù)據(jù)庫表auth_user,用于存儲用戶信息
CREATE TABLE `auth_user` (`id` int NOT NULL AUTO_INCREMENT,`username` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,`password` varchar(500) COLLATE utf8mb4_general_ci DEFAULT NULL,`enabled` tinyint(1) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
④創(chuàng)建用戶實體AuthUser?
@Data
public class AuthUser {@TableId(value = "id", type = IdType.AUTO)private Integer id;private String username;private String password;private Boolean enabled;}
?⑤創(chuàng)建用戶持久層Mapper
package com.yundi.atp.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yundi.atp.entity.AuthUser;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface AuthUserMapper extends BaseMapper<AuthUser> {}
⑥統(tǒng)一響應(yīng)請求返回定義
package com.yundi.atp.commom;import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;@Data
@Builder
public class ApiResponse<T> {@Schema(name = "code", description = "狀態(tài)碼")private Integer code;@Schema(name = "msg", description = "消息結(jié)果")private String msg;@Schema(name = "data", description = "數(shù)據(jù)")private T data;/*** 成功統(tǒng)一響應(yīng)格式** @param* @param <T>* @return*/public static <T> ApiResponse<T> success() {ApiResponse<T> apiResponse = new ApiResponse<>(200, "成功", null);return apiResponse;}/*** 成功統(tǒng)一響應(yīng)格式** @param* @param <T>* @return*/public static <T> ApiResponse<T> success(String msg) {ApiResponse<T> apiResponse = new ApiResponse<>(200, msg, null);return apiResponse;}/*** 成功統(tǒng)一響應(yīng)格式** @param data* @param <T>* @return*/public static <T> ApiResponse<T> success(T data) {ApiResponse<T> apiResponse = new ApiResponse<>(200, "成功", data);return apiResponse;}/*** 成功統(tǒng)一響應(yīng)格式** @param data* @param <T>* @return*/public static <T> ApiResponse<T> success(String msg, T data) {ApiResponse<T> apiResponse = new ApiResponse<>(200, msg, data);return apiResponse;}/*** 失敗統(tǒng)一響應(yīng)格式** @param code* @param message* @param <T>* @return*/public static <T> ApiResponse<T> fail(Integer code, String message) {return new ApiResponse<>(code, message, null);}/*** 失敗統(tǒng)一響應(yīng)格式** @param errorCode* @param <T>* @return*/public static <T> ApiResponse<T> fail(ErrorCode errorCode) {return new ApiResponse<>(errorCode.getCode(), errorCode.getMsg(), null);}
}
⑦ 統(tǒng)一錯誤碼定義
package com.yundi.atp.commom;public enum ErrorCode {SYSTEM_ERROR(10000, "系統(tǒng)錯誤!"),UN_AUTH(10001, "用戶未認(rèn)證,請先登錄!"),AUTH_FAILURE(10002, "認(rèn)證失敗,用戶名或密碼錯誤!"),UN_ACCESS(10003, "該用戶沒有此操作權(quán)限!"),METHOD_ARGS_VALID(10004, "方法參數(shù)驗證失敗!"),TOKEN_VALID(10005, "token鑒權(quán)失敗!"),TOKEN_NOT_EXIST(10006, "token不存在!"),;private Integer code;private String msg;ErrorCode(Integer code, String message) {this.code = code;this.msg = message;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}
}
⑧創(chuàng)建DbUserDetailsManager類實現(xiàn)UserDetailsManager和UserDetailsPasswordService接口方法,用于實現(xiàn)數(shù)據(jù)庫版本的認(rèn)證權(quán)限管理,該類主要是實現(xiàn)用戶數(shù)據(jù)和權(quán)限數(shù)據(jù)的獲取,用于認(rèn)證和授權(quán)使用
package com.yundi.atp.auth;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yundi.atp.entity.AuthUser;
import com.yundi.atp.mapper.AuthUserMapper;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;@Service
public class DbUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {@Resourceprivate AuthUserMapper authUserMapper;@Overridepublic UserDetails updatePassword(UserDetails user, String newPassword) {return null;}@Overridepublic void createUser(UserDetails userDetails) {AuthUser authUser = new AuthUser();authUser.setUsername(userDetails.getUsername());authUser.setPassword(userDetails.getPassword());authUser.setEnabled(true);authUserMapper.insert(authUser);}@Overridepublic void updateUser(UserDetails user) {}@Overridepublic void deleteUser(String username) {}@Overridepublic void changePassword(String oldPassword, String newPassword) {}@Overridepublic boolean userExists(String username) {return false;}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//查詢用戶數(shù)據(jù)QueryWrapper<AuthUser> queryWrapper = new QueryWrapper<>();queryWrapper.eq("username", username);AuthUser authUser = authUserMapper.selectOne(queryWrapper);if (authUser == null) {throw new UsernameNotFoundException(username);} else {//從數(shù)據(jù)庫獲取用戶權(quán)限列表Collection<GrantedAuthority> authorities = new ArrayList<>();authorities.add(() -> "USER_LIST");authorities.add(() -> "USER_ADD");return new User(//用戶賬號authUser.getUsername(),//用戶密碼authUser.getPassword(),//是否啟用authUser.getEnabled(),//用戶賬號是否過期true,//用戶憑證是否過期true,//用戶是否未被鎖定true,//權(quán)限列表authorities);}}
}
⑨創(chuàng)建MyAuthenticationEntryPoint類實現(xiàn)AuthenticationEntryPoint接口,用于未認(rèn)證的處理
package com.yundi.atp.auth;import com.alibaba.fastjson.JSON;
import com.yundi.atp.commom.ApiResponse;
import com.yundi.atp.commom.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Slf4j
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {log.info("未認(rèn)證:" + authException);//返回響應(yīng)response.setContentType("application/json;charset=UTF-8");response.getWriter().println(JSON.toJSONString(ApiResponse.fail(ErrorCode.UN_AUTH)));}
}
?⑩創(chuàng)建MyAuthenticationSuccessHandler類實現(xiàn)AuthenticationSuccessHandler接口,用于認(rèn)證成功的處理
package com.yundi.atp.auth;import com.alibaba.fastjson.JSON;
import com.yundi.atp.commom.ApiResponse;
import com.yundi.atp.util.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Slf4j
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {AuthenticationSuccessHandler.super.onAuthenticationSuccess(request, response, chain, authentication);}@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {//返回響應(yīng)response.setContentType("application/json;charset=UTF-8");response.getWriter().println(JSON.toJSONString(ApiResponse.success("認(rèn)證成功!")));}
}
?創(chuàng)建MyAuthenticationFailureHandler類實現(xiàn)AuthenticationFailureHandler接口,用于認(rèn)證失敗的處理
package com.yundi.atp.auth;import com.alibaba.fastjson.JSON;
import com.yundi.atp.commom.ApiResponse;
import com.yundi.atp.commom.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Slf4j
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {response.setContentType("application/json;charset=UTF-8");response.getWriter().println(JSON.toJSONString(ApiResponse.fail(ErrorCode.AUTH_FAILURE)));}
}
?創(chuàng)建MyLogoutSuccessHandler類實現(xiàn)LogoutSuccessHandler接口,用于退出登錄的處理
package com.yundi.atp.auth;import com.alibaba.fastjson.JSON;
import com.yundi.atp.commom.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Slf4j
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {response.setContentType("application/json;charset=UTF-8");response.getWriter().println(JSON.toJSONString(ApiResponse.success("注銷成功!")));}
}
?創(chuàng)建MyAccessDeniedHandler類實現(xiàn)AccessDeniedHandler接口,用于授權(quán)不通過的處理
package com.yundi.atp.auth;import com.alibaba.fastjson.JSON;
import com.yundi.atp.commom.ApiResponse;
import com.yundi.atp.commom.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Slf4j
public class MyAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {response.setContentType("application/json;charset=UTF-8");response.getWriter().println(JSON.toJSONString(ApiResponse.fail(ErrorCode.UN_ACCESS)));}
}
?創(chuàng)建spring security的配置類,開啟spring security認(rèn)證以及方法級別的授權(quán)
package com.yundi.atp.config;import com.yundi.atp.auth.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.web.SecurityFilterChain;@EnableMethodSecurity
@Configuration
public class SpringSecurityConfig {@Autowiredprivate DbUserDetailsManager dbUserDetailsManager;@Beanpublic AuthenticationManager authenticationManager() {DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();authenticationProvider.setUserDetailsService(dbUserDetailsManager);authenticationProvider.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());return new ProviderManager(authenticationProvider);}@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {//authorizeRequests():開啟授權(quán)保護(hù)//anyRequest():對所有請求開啟授權(quán)保護(hù)//authenticated():已認(rèn)證請求會自動被授權(quán)http.authorizeRequests(authorize -> {//放行路徑authorize.antMatchers("/webjars/**", "/v3/**", "/doc.html", "/favicon.ico", "/swagger-ui/**").permitAll();//其它請求需要認(rèn)證authorize.anyRequest().authenticated();});//自定義登錄http.formLogin(formLogin -> {formLogin.loginPage("/login").permitAll() //登錄頁面無需授權(quán)即可訪問.usernameParameter("username") //自定義表單用戶名參數(shù),默認(rèn)是username.passwordParameter("password");//自定義表單密碼參數(shù),默認(rèn)是password//登錄認(rèn)證成功formLogin.successHandler(new MyAuthenticationSuccessHandler());//登錄認(rèn)證失敗formLogin.failureHandler(new MyAuthenticationFailureHandler());});// 基本授權(quán)方式http.httpBasic(Customizer.withDefaults());//自定義注銷http.logout(logout -> {logout.logoutUrl("/logout");//刪除授權(quán)信息logout.clearAuthentication(true);//刪除cookielogout.deleteCookies("JSESSIONID");//設(shè)置session失效logout.invalidateHttpSession(true);//退出的處理logout.addLogoutHandler(new MyLogoutHandler());//退出成功的響應(yīng)logout.logoutSuccessHandler(new MyLogoutSuccessHandler());});//錯誤處理http.exceptionHandling(exception -> {//請求未認(rèn)證的接口exception.authenticationEntryPoint(new MyAuthenticationEntryPoint());//未授權(quán)的接口異常處理:可能會不生效,被全局異常處理器攔截exception.accessDeniedHandler(new MyAccessDeniedHandler());});// 跨域http.cors(Customizer.withDefaults());//關(guān)閉csrf攻擊防御http.csrf(csrf -> {csrf.disable();});return http.build();}}
?spring security的配置說明
1.開啟方法級別的權(quán)限驗證
2.注入自定義的數(shù)據(jù)庫版本的授權(quán)管理器
3.放行不需要認(rèn)證的路徑
4.配置登錄的認(rèn)證及認(rèn)證處理
5.配置基本的授權(quán)方式
6.配置用戶注銷的處理
7.異常的處理
8.跨域的處理
9.關(guān)閉csrf攻擊防御
?使用PasswordEncoderFactories工廠生成一個spring security的密碼,創(chuàng)建一個用戶用戶測試
?訪問未認(rèn)證的請求
?使用正確的用戶名和密碼訪問登錄接口/login,注意這里必須是post請求
?使用錯誤的用戶名和密碼訪問登錄接口/login,注意這里必須是post請求
?測試接口授權(quán),目前的權(quán)限包含USER_LIST、USER_ADD,在DbUserDetailsManager中查詢的
?訪問/logout注銷接口,查看結(jié)果
結(jié)語
關(guān)于springboot前后端分離方式項目集成spring securtity安全框架的內(nèi)容到這里就結(jié)束了,我們下期見。。。。。。