傳統的應用是将Session放在應用伺服器上,而将生成的JSESSIONID放在使用者浏覽器的Cookie中,而這種模式在前後端分離中就會出現以下問題
1,開發繁瑣。
2,安全性和客戶體驗差
3,有些前端技術不支援Cookie,如微信小程式
這種情況下,前後端之間使用Token(令牌)進行通信就完美的解決上面的問題。
⒈添加pom依賴1 <dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-security</artifactId>
4 </dependency>
5 <dependency>
6 <groupId>org.springframework.boot</groupId>
7 <artifactId>spring-boot-starter-web</artifactId>
8 </dependency>
9 <dependency>
10 <groupId>org.springframework.security.oauth</groupId>
11 <artifactId>spring-security-oauth2</artifactId>
12 <version>2.3.5.RELEASE</version>
13 </dependency>
14 <dependency>
15 <groupId>commons-collections</groupId>
16 <artifactId>commons-collections</artifactId>
17 <version>3.2.2</version>
18 </dependency>
19 <dependency>
20 <groupId>org.springframework.boot</groupId>
21 <artifactId>spring-boot-starter-test</artifactId>
22 <scope>test</scope>
23 </dependency>
24 <dependency>
25 <groupId>org.springframework.security</groupId>
26 <artifactId>spring-security-test</artifactId>
27 <scope>test</scope>
28 </dependency>
⒉編寫AuthenticationSuccessHandler的實作
1 package cn.coreqi.handler;
2
3 import com.fasterxml.jackson.databind.ObjectMapper;
4 import org.apache.commons.codec.binary.StringUtils;
5 import org.apache.commons.collections.MapUtils;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8 import org.springframework.beans.factory.annotation.Autowired;
9 import org.springframework.security.authentication.BadCredentialsException;
10 import org.springframework.security.core.Authentication;
11 import org.springframework.security.oauth2.common.OAuth2AccessToken;
12 import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
13 import org.springframework.security.oauth2.provider.*;
14 import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
15 import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
16 import org.springframework.stereotype.Component;
17 import javax.servlet.ServletException;
18 import javax.servlet.http.HttpServletRequest;
19 import javax.servlet.http.HttpServletResponse;
20 import java.io.IOException;
21 import java.util.Base64;
22
23 @Component("coreqiAuthenticationSuccessHandler")
24 public class CoreqiAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
25
26 private Logger logger = LoggerFactory.getLogger(getClass());
27
28 @Autowired
29 private ClientDetailsService clientDetailsService;
30
31 @Autowired
32 private AuthorizationServerTokenServices authorizationServerTokenServices;
33
34 @Autowired
35 private ObjectMapper objectMapper; //将對象轉換為Json的工具類,SpringMVC在啟動的時候會自動為我們注冊ObjectMapper
36
37 /**
38 * @param request 不知道
39 * @param response 不知道
40 * @param authentication Authentication接口是SpringSecurity的一個核心接口,它的作用是封裝我們的認證資訊,包含認證請求中的一些資訊,包括認證請求的ip,Session是什麼,以及認證使用者的資訊等等。
41 * @throws IOException
42 * @throws ServletException
43 */
44 @Override
45 public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
46 //1.從請求參數中拿到ClientId
47 String header = request.getHeader("Authorization");
48 if (header == null && !header.toLowerCase().startsWith("basic ")) {
49 throw new UnapprovedClientAuthenticationException("請求頭中無client資訊!");
50 }
51 String[] tokens = this.extractAndDecodeHeader(header, request);
52 assert tokens.length == 2;
53
54 String clientId = tokens[0];
55 String clientSecret = tokens[1];
56
57 //2.通過ClientId拿到ClientDetails
58 ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
59 if(clientDetails == null){
60 throw new UnapprovedClientAuthenticationException("clientId對應的配置資訊不存在:" + clientId);
61 }else if(!StringUtils.equals(clientDetails.getClientSecret(),clientSecret)){
62 throw new UnapprovedClientAuthenticationException("clientSecret不比對:" + clientId);
63 }
64 //3.建立TokenRequest
65 TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP,clientId,clientDetails.getScope(),"custom");
66
67 //4.建構OAuth2Request
68 OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
69
70 //5.建構OAuth2Authentication
71 OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request,authentication);
72
73 //6.建構OAuth2AccessToken
74 OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
75
76 //7.将生成的Token傳回給請求
77 response.setContentType("application/json;charset=UTF-8");
78 response.getWriter().write(objectMapper.writeValueAsString(token));
79 }
80
81 /**
82 * 從請求頭中解析使用者名密碼
83 * @param header
84 * @param request
85 * @return
86 * @throws IOException
87 */
88 private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException {
89 byte[] base64Token = header.substring(6).getBytes("UTF-8");
90
91 byte[] decoded;
92 try {
93 decoded = Base64.getDecoder().decode(base64Token);
94 } catch (IllegalArgumentException var7) {
95 throw new BadCredentialsException("Failed to decode basic authentication token");
96 }
97
98 String token = new String(decoded, "UTF-8");
99 int delim = token.indexOf(":");
100 if (delim == -1) {
101 throw new BadCredentialsException("Invalid basic authentication token");
102 } else {
103 return new String[]{token.substring(0, delim), token.substring(delim + 1)};
104 }
105 }
106
107 }
⒊配置Security
1 package cn.coreqi.config;
2
3 import org.springframework.context.annotation.Bean;
4 import org.springframework.security.authentication.AuthenticationManager;
5 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
6 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
7 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
8 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
9 import org.springframework.security.crypto.password.NoOpPasswordEncoder;
10 import org.springframework.security.crypto.password.PasswordEncoder;
11
12 @EnableWebSecurity
13 public class CoreqiWebSecurityConfig extends WebSecurityConfigurerAdapter {
14
15 @Override
16 @Bean
17 public AuthenticationManager authenticationManagerBean() throws Exception {
18 return super.authenticationManagerBean();
19 }
20
21 @Override
22 protected void configure(HttpSecurity http) throws Exception {
23 http.httpBasic()
24 .and()
25 .authorizeRequests()
26 .antMatchers("/oauth/token","/login").permitAll()
27 .anyRequest().authenticated() //任何請求都需要身份認證
28 .and().csrf().disable(); //禁用CSRF
29 }
30
31 @Override
32 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
33 auth.inMemoryAuthentication()
34 .withUser("fanqi").password("admin").roles("admin");
35 }
36
37 @Bean
38 public PasswordEncoder passwordEncoder()
39 {
40 return NoOpPasswordEncoder.getInstance();
41 }
42 }
⒋配置OAuth2
1 package cn.coreqi.config;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.beans.factory.annotation.Qualifier;
5 import org.springframework.context.annotation.Configuration;
6 import org.springframework.security.authentication.AuthenticationManager;
7 import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
8 import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
9 import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
10 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
11 import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
12
13 @Configuration
14 @EnableAuthorizationServer //開啟認證伺服器
15 public class CoreqiAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
16
17 @Autowired
18 @Qualifier("authenticationManagerBean")
19 private AuthenticationManager authenticationManager;
20
21 @Autowired
22 private AuthenticationConfiguration authenticationConfiguration;
23
24 /**
25 * password模式需要提供一個AuthenticationManager到AuthorizationServerEndpointsConfigurer
26 * @param authorizationServerEndpointsConfigurer
27 * @throws Exception
28 */
29 @Override
30 public void configure(AuthorizationServerEndpointsConfigurer authorizationServerEndpointsConfigurer) throws Exception {
31 authorizationServerEndpointsConfigurer.authenticationManager(authenticationConfiguration.getAuthenticationManager());
32 }
33
34 @Override
35 public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception {
36 clientDetailsServiceConfigurer.inMemory()
37 .withClient("coreqi")
38 .secret("coreqiSecret")
39 .redirectUris("https://www.baidu.com")
40 .scopes("ALL")
41 .authorities("COREQI_READ")
42 .authorizedGrantTypes("authorization_code","password");
43 }
44
45 }
⒌配置資源伺服器
1 package cn.coreqi.config;
2
3 import cn.coreqi.handler.CoreqiAuthenticationSuccessHandler;
4 import org.springframework.beans.factory.annotation.Autowired;
5 import org.springframework.context.annotation.Configuration;
6 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
7 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
8 import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
9 import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
10
11 @Configuration
12 @EnableResourceServer //開啟資源伺服器
13 public class CoreqiResourceServerConfig extends ResourceServerConfigurerAdapter {
14
15 @Autowired
16 private CoreqiAuthenticationSuccessHandler coreqiAuthenticationSuccessHandler;
17
18 @Override
19 public void configure(HttpSecurity http) throws Exception {
20 http.formLogin()
21 .successHandler(coreqiAuthenticationSuccessHandler)
22 .and()
23 .authorizeRequests()
24 .antMatchers("/oauth/token","/login").permitAll()
25 .anyRequest().authenticated() //任何請求都需要身份認證
26 .and()
27 .csrf()
28 .disable(); //禁用CSRF
29 }
30
31 }
⒍測試
作者:奇
出處:https://www.cnblogs.com/fanqisoft/
本文版權歸作者和部落格園共有,歡迎轉載,但必須給出原文連結,并保留此段聲明,否則保留追究法律責任的權利。