主頁 > 軟體設計 > 手把手教你如何使用Spring Security(上):登錄授權

手把手教你如何使用Spring Security(上):登錄授權

2021-04-28 14:05:33 軟體設計

文章目錄

  • 一、什么是 Spring Security?
    • 官方介紹
    • 通俗來講
  • 二、初始搭建
    • 創建
    • 啟動
  • 三、專案原理
    • 原理
    • 思考
  • 四、登錄認證
    • 登錄過濾器
    • 配置過濾器鏈
    • 類補充
  • 五、登錄效果
    • 效果演示
    • 請求后臺介面
  • 末、系列文章


一、什么是 Spring Security?

官方介紹

  • Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Spring Security是一個功能強大且高度可定制的身份認證和訪問控制框架,它是保護基于spring應用程式的事實標準,

  • Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements.

Spring Security是一個重點為Java應用程式提供認證和授權的框架,與所有Spring專案一樣,Spring Security的真正強大之處在于它可以很容易地擴展以滿足定制需求,

通俗來講

  • 首先我們前端應用訪問后臺資源,比如后臺介面,是需要帶上訪問憑證(令牌)的,我們肯定不能夠直接將介面資源暴露給前端應用,這有很大安全隱患,
  • 這個框架就是方便了獲取憑證流程,其重點在于認證和授權,認證就是對你的身份進行認證,比如校驗用戶名密碼是否正確、手機號是否正確、是否在我們庫中存在該手機號用戶,認證的目的就是為了授權,授權的結果就是給到前端一個訪問憑證,比如給到前端一個JWT令牌,
  • 前端有了這個令牌,每次請求帶上令牌就可以訪問后臺資源了,當然每次訪問資源之前,后臺都會對這個令牌進行一個校驗,判斷這個令牌是否有效是否已過期等等,
  • 可以暫時把它單純的理解為 登錄認證 用的,

二、初始搭建

創建

  • 創建一個Maven空專案,引入依賴,創建主類
    初始專案

  • 引入兩個依賴 spring-boot-starter-webspring-boot-starter-security

	<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>1.5.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>1.5.9.RELEASE</version>
        </dependency>
    </dependencies>
  • 創建啟動類,同時定義一個 /hello 介面
@SpringBootApplication
@RestController
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @GetMapping("/hello")
    public String hello() {
        return "Hello Spring Security!";
    }
}

啟動

  • 啟動日志
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.9.RELEASE)

2021-04-24 10:38:10.193  INFO 7684 --- [           main] com.meicloud.security.DemoApplication    : Starting DemoApplication on DESKTOP-T2KEH3M with PID 7684 (C:\Users\85176\Desktop\spring-security-demo\target\classes started by 85176 in C:\Users\85176\Desktop\spring-security-demo)
2021-04-24 10:38:10.195  INFO 7684 --- [           main] com.meicloud.security.DemoApplication    : No active profile set, falling back to default profiles: default
2021-04-24 10:38:10.237  INFO 7684 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6d4d66d2: startup date [Sat Apr 24 10:38:10 CST 2021]; root of context hierarchy
2021-04-24 10:38:11.337  INFO 7684 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2021-04-24 10:38:11.347  INFO 7684 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-04-24 10:38:11.348  INFO 7684 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.23
2021-04-24 10:38:11.461  INFO 7684 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-04-24 10:38:11.461  INFO 7684 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1227 ms
2021-04-24 10:38:11.679  INFO 7684 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2021-04-24 10:38:11.679  INFO 7684 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2021-04-24 10:38:11.679  INFO 7684 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2021-04-24 10:38:11.679  INFO 7684 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2021-04-24 10:38:11.680  INFO 7684 --- [ost-startStop-1] .s.DelegatingFilterProxyRegistrationBean : Mapping filter: 'springSecurityFilterChain' to: [/*]
2021-04-24 10:38:11.680  INFO 7684 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2021-04-24 10:38:11.963  INFO 7684 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6d4d66d2: startup date [Sat Apr 24 10:38:10 CST 2021]; root of context hierarchy
2021-04-24 10:38:12.043  INFO 7684 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/hello],methods=[GET]}" onto public java.lang.String com.meicloud.security.DemoApplication.hello()
2021-04-24 10:38:12.047  INFO 7684 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2021-04-24 10:38:12.048  INFO 7684 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2021-04-24 10:38:12.101  INFO 7684 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2021-04-24 10:38:12.101  INFO 7684 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2021-04-24 10:38:12.158  INFO 7684 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2021-04-24 10:38:12.371  INFO 7684 --- [           main] b.a.s.AuthenticationManagerConfiguration : 

Using default security password: 03deaaa0-d17f-445a-abf0-f21b2a6cf554

2021-04-24 10:38:12.424  INFO 7684 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/css/**'], Ant [pattern='/js/**'], Ant [pattern='/images/**'], Ant [pattern='/webjars/**'], Ant [pattern='/**/favicon.ico'], Ant [pattern='/error']]], []
2021-04-24 10:38:12.522  INFO 7684 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/**']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@3e15bb06, org.springframework.security.web.context.SecurityContextPersistenceFilter@2cfa2c4f, org.springframework.security.web.header.HeaderWriterFilter@66c38e51, org.springframework.security.web.authentication.logout.LogoutFilter@5fe7f967, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@3f049056, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@48eb9836, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@79d06bbd, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6778aea6, org.springframework.security.web.session.SessionManagementFilter@12968227, org.springframework.security.web.access.ExceptionTranslationFilter@58cf8f94, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@27b45ea]
2021-04-24 10:38:12.664  INFO 7684 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2021-04-24 10:38:12.716  INFO 7684 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2021-04-24 10:38:12.720  INFO 7684 --- [           main] com.meicloud.security.DemoApplication    : Started DemoApplication in 2.804 seconds (JVM running for 5.014)
  • 訪問介面 http://localhost:8080/hello,能彈出這個登錄框就說明專案已經被Spring Security應用了,其實這個登錄認證使用的是默認的登錄過濾器,末尾放出相關原始碼文章,如果有興趣可以了解了解是怎么配置的默認過濾器,
    登錄頁面
  • 默認用戶名是 user,密碼在控制臺已列印出來了,輸入即可登錄,登錄后即可訪問后臺資源,
    hello介面

三、專案原理

原理

在實戰之前你需要了解一些關于這個框架的原理,其實這個框架的核心是創建一個 FilterChainProxy 型別的過濾器,這個過濾器里面維護了一組過濾器鏈,而我們要做的是 創建一條我們自己的過濾器鏈 ,當然創建流程框架幫我們搞定了,我們要做的是把自己的過濾器鏈配置進框架,由框架幫我們創建出過濾器鏈,因此:

  • 目的很明確:創建一條過濾器鏈,
  • 操作很簡單:配置我們的過濾器鏈到框架,

思考

只是要寫一個登錄介面,也就是一個過濾器就行了,為什么要配置一條過濾器鏈?

  • 其實你確實只要寫一個登錄過濾器就行,但還是要加入到過濾器鏈中,你也需要(可選)很多其他的過濾器,比如請求頭過濾器、跨域資源共享過濾器、登出過濾器、Session過濾器等等,這些過濾器框架都已經提供了,可以直接配置,
  • 你要知道使用這個框架的目的,一個是方便搭建我們的過濾器,一個是它提供了很多默認的強大的過濾器,不用我們重新寫,這是我們用這個框架的原因,大概就是圖個方便,

四、登錄認證

Github專案地址:spring-security-demo,其中類里完善了大量詳細的描述,如果有用請默默點個贊,如果不理解可以評論一起探討,

首先明確要做的是 創建一個登錄過濾器配置一條過濾器鏈 ,大致看看要創建的類,接下來一個一個了解:
專案類組成

登錄過濾器

完整的登錄過濾器應該包含很多組成部分,包括過濾器本身(UnionidAuthenticationFilter),認證用的Provider(UnionidAuthenticationProvider),登錄成功處理器(UnionidLoginSuccessHandler),登錄失敗處理器(HttpStatusLoginFailureHandler),還有最后一個過濾器配置器(UnionidLoginConfigurer),一個一個來說:

  • 登錄過濾器:一般會繼承抽象類 AbstractAuthenticationProcessingFilter,實作它的 attemptAuthentication 方法,登錄的URL是 /user/members:login
/**
 * 登錄認證過濾器
 */
public class UserAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

	public UserAuthenticationFilter() {
		super(new AntPathRequestMatcher("/user/login", "POST"));
	}

	@Override
	public void afterPropertiesSet() {
		Assert.notNull(this.getAuthenticationManager(), "AuthenticationManager must be specified");
		Assert.notNull(this.getSuccessHandler(), "AuthenticationSuccessHandler must be specified");
		Assert.notNull(this.getFailureHandler(), "AuthenticationFailureHandler must be specified");
	}

	@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException, IOException, ServletException {

		// TODO 這里的邏輯主要有兩個作用,一個是進行初步的校驗,一個是組裝待認證的Token,舉幾個例子:

		// 1.微信授權登錄:客戶端會傳過來一些加密串,這里邏輯主要解密這些加密串的資料獲取unionId、openId、手機號以及用戶昵稱頭像等基本資訊,
		// 然后組裝Token傳給Provider進行下一步認證,如果這里報錯直接就回傳例外,不會進行下一步認證,

		// 2.手機短信驗證碼登錄:這里主要驗證短信驗證碼的正確性,然后組裝Token傳給Provider進行下一步認證,如果短信驗證碼錯誤直接拋例外

		// 3.賬號密碼圖形驗證碼登錄:這里主要驗證圖形驗證碼的正確性,然后組裝Token傳給Provider進行下一步認證,如果圖形驗證碼錯誤直接拋例外

		// ...

		// =================================================== 示例 ===============================================

		String body = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
		String mobile = null, password = null, verifyCode = null;

		if(StringUtils.hasText(body)) {
			UserLoginRequest loginRequest = JSON.parseObject(body, UserLoginRequest.class);
			mobile = loginRequest.getMobile();
			password = loginRequest.getPassword();
			verifyCode = loginRequest.getVerifyCode();
		}

		// TODO 這里驗證圖形驗證碼 verifyCode 是否正確

		UserAuthenticationToken token = new UserAuthenticationToken(
				null, mobile, password);

		// 這里進行下一步認證,會走到我們定義的 UserAuthenticationProvider 中
		return this.getAuthenticationManager().authenticate(token);
	}

}
  • 認證用的Provider:需要實作 AuthenticationProvider 介面,實作 authenticate()supports() 方法,
/**
 * Unionid認證 Provider
 */
public class UserAuthenticationProvider implements AuthenticationProvider {

	public UserAuthenticationProvider() {

	}

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {

		// TODO 這里主要進行一個資料庫層面的認證,比如賬號密碼的正確性,比如該賬號是否被拉黑有什么權限等,都認證成功之后會組裝一個認證通過的 Token

		// 這里認證成功回傳之后會跑到成功處理器:UserLoginSuccessHandler
		// 只要整個認證(包括前面的校驗)中有一個地方拋出例外都會呼叫失敗處理器:HttpStatusLoginFailureHandler

		// =================================================== 示例 ===============================================

		UserAuthenticationToken token = (UserAuthenticationToken) authentication;

		// 校驗賬號密碼是否正確,同時回傳用戶資訊
		UserInfoDTO userInfo = this.checkAndGetUserInfo(token.getMobile(), token.getPassword());

		// 組裝并回傳認證成功的 Token
		JwtUserLoginDTO jwtUserLoginDTO = new JwtUserLoginDTO(userInfo.getUserId(), userInfo.getNickname(), userInfo.getMobile());

		return new JwtAuthenticationToken(jwtUserLoginDTO, null, null);
	}

	private UserInfoDTO checkAndGetUserInfo(String mobile, String password) {

		// 根據手機號查詢用戶資訊,這里假設是根據手機號從資料庫中查出的用戶資訊
		UserInfoDTO userInfo = null;
		if (mobile.equals("15600000000")) {
			userInfo = new UserInfoDTO(100000000L, "張三", "15600000000", "888888");
		}
		if (Objects.isNull(userInfo)) {
			throw LoginAuthenticationException.USER_NAME_NOT_EXIST;
		}
		// 校驗密碼是否正確
		if (!Objects.equals(userInfo.getPassword(), password)) {
			// 密碼不正確直接拋例外
			throw LoginAuthenticationException.PASSWORD_NOT_EXIST;
		}

		return userInfo;
	}

	/**
	 * 表示這個 Provider 支持認證的 Token(這里是 UserAuthenticationToken)
	 *
	 * @param authentication
	 * @return
	 */
	@Override
	public boolean supports(Class<?> authentication) {
		return authentication.isAssignableFrom(UserAuthenticationToken.class);
	}
}
  • 登錄成功處理器:需要實作 AuthenticationSuccessHandler 介面同時實作 onAuthenticationSuccess() 方法,
/**
 * 登錄成功處理器
 */
public class UserLoginSuccessHandler implements AuthenticationSuccessHandler{

	public static final String HEADER_SET_ACCESS_TOKEN = "Set-Access-Token";

	private SecurityConfig securityConfig;

	public UserLoginSuccessHandler(SecurityConfig securityConfig) {
		this.securityConfig = securityConfig;
	}

	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) throws IOException, ServletException {

		// TODO 走到這里說明認證成功,可以組裝一些回應頭的資訊給到客戶端,比如生成JWT令牌,或者加一些業務上的需求,比如登錄送積分等等

		// =================================================== 示例 ===============================================

		// 這里的邏輯是生成JWT令牌(很多公司也會用Session),將生成的JWT回傳給前端
		Date expiredDate = new Date(System.currentTimeMillis() + securityConfig.getTokenExpireTimeInSecond() * 1000);
		Algorithm algorithm = Algorithm.HMAC256(securityConfig.getTokenEncryptSalt());

		JwtUserLoginDTO jwtUserLoginDTO = (JwtUserLoginDTO) authentication.getPrincipal();
		String token = jwtUserLoginDTO.sign(algorithm, expiredDate);

		// 設定請求頭,將JWT令牌以請求頭的方式回傳給前端
		response.addHeader(HEADER_SET_ACCESS_TOKEN, token);

	}
}
  • 登錄失敗處理器:實作 AuthenticationFailureHandler 介面同時實作 onAuthenticationFailure 方法,
/**
 * 登錄失敗處理器
 */
public class HttpStatusLoginFailureHandler implements AuthenticationFailureHandler{

	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException exception) throws IOException, ServletException {

		// TODO 走到這里說明認證流程失敗了,會對例外資訊做一個統一的處理,通過 response 寫回到客戶端

		// =================================================== 示例 ===============================================

		response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
		response.setContentType("application/json");
		response.setCharacterEncoding(Charset.defaultCharset().displayName());

		if (exception instanceof LoginAuthenticationException) {
			LoginAuthenticationException e = (LoginAuthenticationException) exception;
			response.getWriter().print(e.toJSONString());
		}
		response.getWriter().print("登錄例外!");
	}
}
  • 過濾器配置器:一般繼承 AbstractHttpConfigurer 抽象類,實作 configure() 方法,主要配置成功處理器和失敗處理器,同時將登錄過濾器配置進 HttpSecurity
/**
 * 登錄過濾器配置
 */
public class UserLoginConfigurer<T extends UserLoginConfigurer<T, B>, B extends HttpSecurityBuilder<B>> extends AbstractHttpConfigurer<T, B>  {

	private SecurityConfig securityConfig;

	public UserLoginConfigurer(SecurityConfig securityConfig) {
		this.securityConfig = securityConfig;
	}

	@Override
	public void configure(B http) throws Exception {

		UserAuthenticationFilter authFilter = new UserAuthenticationFilter();

		authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
		authFilter.setSessionAuthenticationStrategy(new NullAuthenticatedSessionStrategy());

		// 登錄成功處理器
		authFilter.setAuthenticationSuccessHandler(new UserLoginSuccessHandler(securityConfig));
		// 登錄失敗處理器
		authFilter.setAuthenticationFailureHandler(new HttpStatusLoginFailureHandler());

		// 攔截器位置
		UserAuthenticationFilter filter = postProcess(authFilter);
		http.addFilterAfter(filter, LogoutFilter.class);
	}
}

配置過濾器鏈

  • 主要配置你需要的過濾器,以及自定義的登錄過濾器,也可以配置哪些URL不應該被過濾器鏈攔截,一般會繼承 WebSecurityConfigurerAdapter 抽象類,
  • 覆寫 configure(HttpSecurity http) 方法配置過濾器鏈
  • 注意還要覆寫 configure(AuthenticationManagerBuilder auth) 方法將前面定義的 UnionidAuthenticationProvider 配置進 AuthenticationManagerBuilder
/**
 * 核心配置器
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private SecurityConfig securityConfig;

	@Override
	protected void configure(HttpSecurity http) throws Exception {
				// 配置白名單(比如登錄介面)
				.antMatchers(securityConfig.getPermitUrls()).permitAll()
				// 其他URL需要認證通過才能訪問后臺資源
				.anyRequest().authenticated()
				.and()
				// 禁用跨站點偽造請求
				.csrf().disable()
				// 啟用跨域資源共享
				.cors()
				.and()
				// 請求頭
				.headers().addHeaderWriter(
				new StaticHeadersWriter(Collections.singletonList(
						new Header("Access-control-Allow-Origin", "*"))))
				.and()
			 	// 自定義的登錄過濾器,不同的登錄方式創建不同的登錄過濾器,一樣的配置方式
				.apply(new UserLoginConfigurer<>(securityConfig))
				.and()
				// 登出過濾器
				.logout()
				// 登出成功處理器
				.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())
				.and()
				// 禁用Session會話機制(我們這個demo用的是JWT令牌的方式)
				.sessionManagement().disable()
				// 禁用SecurityContext,這個配置器實際上認證資訊會保存在Session中,但我們并不用Session機制,所以也禁用
				.securityContext().disable();
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(userAuthenticationProvider());
	}

	@Bean
	protected AuthenticationProvider userAuthenticationProvider() throws Exception{
		return new UserAuthenticationProvider();
	}

	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
	    return super.authenticationManagerBean();
	}

	@Bean
	protected CorsConfigurationSource corsConfigurationSource() {
		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowedOrigins(Collections.singletonList("*"));
		configuration.setAllowedMethods(Arrays.asList("GET","POST","HEAD", "DELETE", "PUT","OPTION"));
		configuration.setAllowedHeaders(Collections.singletonList("*"));
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}

}

類補充

由于篇幅太長,其他類請下載原始碼查看,Github專案地址:spring-security-demo


五、登錄效果

本專案用的是JWT,登錄成功后會在回應頭回傳JWT令牌,失敗則顯示錯誤資訊,其中目前后臺模擬了一個用戶名為 15600000000,密碼為 888888 的用戶,注意后臺并沒有校驗驗證碼,這部分請自行完善,來看看演示效果,

效果演示

  • 用戶名輸入錯誤
    用戶名輸入錯誤
  • 密碼輸入錯誤
    密碼錯誤
  • 登錄成功,回傳令牌
    登錄成功

請求后臺介面

  • 登錄成功后訪問 /hello 介面
    訪問資源介面
  • 為什么登錄成功了,還是禁止訪問?

其實原因很簡單,這個資源介面沒有帶上登錄回傳的JWT令牌,就算帶上了后臺也沒有識別這個令牌的邏輯,也即后臺還是無法識別普通請求,所以需要加上 識別請求令牌的邏輯 ,預知后續邏輯,請看下篇講解~


末、系列文章

  • 使用 Spring Security 系列文章:

手把手教你如何使用Spring Security(上):登錄授權
手把手教你如何使用Spring Security(下):介面認證

  • 如果想深入了解 Spring Security 原始碼,使用的時候為什么需要繼承各種類介面,請參考:

《Spring Security原始碼(一):整體框架設計》
《Spring Security原始碼(二):建造者詳解》
《Spring Security原始碼(三):HttpSecurity詳解》
《Spring Security原始碼(四):配置器詳解》
《Spring Security原始碼(五):FilterChainProxy是如何創建的?》
《Spring Security原始碼(六):FilterChainProxy是如何運行的?》
《Spring Security原始碼(七):設計模式在框架中的應用》

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/280944.html

標籤:其他

上一篇:MyBatis常見面試題

下一篇:阿里釘釘Android實習面試也太太太太難了吧,對演算法的要求堪比位元組

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more