欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

spring security在分布式項(xiàng)目下的配置方法(案例詳解)

 更新時(shí)間:2020年10月10日 10:09:52   作者:藍(lán)天白水  
這篇文章主要介紹了spring security在分布式項(xiàng)目下的配置方法,本文通過一個(gè)項(xiàng)目案例給大家詳細(xì)介紹,通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

分布式項(xiàng)目和傳統(tǒng)項(xiàng)目的區(qū)別就是,分布式項(xiàng)目有多個(gè)服務(wù),每一個(gè)服務(wù)僅僅只實(shí)現(xiàn)一套系統(tǒng)中一個(gè)或幾個(gè)功能,所有的服務(wù)組合在一起才能實(shí)現(xiàn)系統(tǒng)的完整功能。這會(huì)產(chǎn)生一個(gè)問題,多個(gè)服務(wù)之間session不能共享,你在其中一個(gè)服務(wù)中登錄了,登錄信息保存在這個(gè)服務(wù)的session中,別的服務(wù)不知道啊,所以你訪問別的服務(wù)還得在重新登錄一次,對用戶十分不友好。為了解決這個(gè)問題,于是就產(chǎn)生了單點(diǎn)登錄:

**jwt單點(diǎn)登錄:**就是用戶在登錄服務(wù)登錄成功后,登錄服務(wù)會(huì)產(chǎn)生向前端響應(yīng)一個(gè)token(令牌),以后用戶再訪問系統(tǒng)的資源的時(shí)候都要帶上這個(gè)令牌,各大服務(wù)對這個(gè)令牌進(jìn)行驗(yàn)證(令牌是否過期,令牌是否被篡改),驗(yàn)證通過了,可以訪問資源,同時(shí),令牌中也會(huì)攜帶一些不重要的信息,比如用戶名,權(quán)限。通過解析令牌就能知道當(dāng)前登錄的用戶和用戶所擁有的權(quán)限。

下面我們就來寫一個(gè)案例項(xiàng)目看看具體如何使用

1 創(chuàng)建項(xiàng)目結(jié)構(gòu)

 1.1 父工程cloud-security

這是父工程所需要的包

<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.1.3.RELEASE</version>
 <relativePath/>
</parent>

<dependencies>
 
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
 </dependency>

 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
 </dependency>
</dependencies>

1.2 公共工程 security-common

這是公共工程所需要的包

<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
</dependency>

<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>fastjson</artifactId>
 <version>1.2.60</version>
</dependency>

<!--jwt所需包-->
<dependency>
 <groupId>io.jsonwebtoken</groupId>
 <artifactId>jjwt-api</artifactId>
 <version>0.11.2</version>
</dependency>
<dependency>
 <groupId>io.jsonwebtoken</groupId>
 <artifactId>jjwt-impl</artifactId>
 <version>0.11.2</version>
 <scope>runtime</scope>
</dependency>
<dependency>
 <groupId>io.jsonwebtoken</groupId>
 <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
 <version>0.11.2</version>
 <scope>runtime</scope>
</dependency>

1.3 認(rèn)證服務(wù)security-sever

這個(gè)服務(wù)僅僅只有兩項(xiàng)功能:

(1)用戶登錄,頒發(fā)令牌

(2)用戶注冊

我們這里只實(shí)現(xiàn)第一個(gè)功能

1.3.1 認(rèn)證服務(wù)所需的包

<dependency>
 <groupId>cn.lx.security</groupId>
 <artifactId>security-common</artifactId>
 <version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--通用mapper-->
<dependency>
 <groupId>tk.mybatis</groupId>
 <artifactId>mapper-spring-boot-starter</artifactId>
 <version>2.0.4</version>
</dependency>

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

1.3.2 配置application.yml

這里面的配置沒什么好說的,都很簡單

server:
 port: 8080

spring:
 datasource:
 url: jdbc:mysql:///security_authority?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
 username: root
 password:
 driver-class-name: com.mysql.cj.jdbc.Driver
 thymeleaf:
 cache: false
 main:
 allow-bean-definition-overriding: true


mybatis:
 type-aliases-package: cn.lx.security.doamin
 configuration:
 #駝峰
 map-underscore-to-camel-case: true

logging:
 level:
 cn.lx.security: debug

1.3.3 導(dǎo)入domain,dao,service,config

這個(gè)可以在上篇文檔中找到,我們只需要service中的loadUserByUsername方法及其所調(diào)用dao中的方法

完整項(xiàng)目在我的github中,地址:git@github.com:lx972/cloud-security.git

配置文件我們也從上篇中復(fù)制過來MvcConfig,SecurityConfig

1.3.4 測試

訪問http://localhost:8080/loginPage成功出現(xiàn)登錄頁面,說明認(rèn)證服務(wù)的骨架搭建成功了

1.4 資源服務(wù)security-resource1

實(shí)際項(xiàng)目中會(huì)有很多資源服務(wù),我只演示一個(gè)

為了簡單,資源服務(wù)不使用數(shù)據(jù)庫

1.4.1 資源服務(wù)所需的包

<dependency>
 <groupId>cn.lx.security</groupId>
 <artifactId>security-common</artifactId>
 <version>1.0-SNAPSHOT</version>
</dependency>

1.4.2 配置application.yml

server:
 port: 8090

logging:
 level:
 cn.lx.security: debug

1.4.3 controller

擁有ORDER_LIST權(quán)限的才能訪問

@RestController
@RequestMapping("/order")
public class OrderController {

 //@Secured("ORDER_LIST")
 @PreAuthorize(value = "hasAuthority('ORDER_LIST')")
 @RequestMapping("/findAll")
 public String findAll(){
  return "order-list";
 }
}

擁有PRODUCT_LIST權(quán)限的才能訪問

@RestController
@RequestMapping("/product")
public class ProductController {

 //@Secured("PRODUCT_LIST")
 @PreAuthorize(value = "hasAuthority('PRODUCT_LIST')")
 @RequestMapping("/findAll")
 public String findAll(){
  return "product-list";
 }
}

1.4.4 security配置類

@Configuration
@EnableWebSecurity
//這個(gè)注解先不要加
//@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
 /**
  * Override this method to configure the {@link HttpSecurity}. Typically subclasses
  * should not invoke this method by calling super as it may override their
  * configuration. The default configuration is:
  *
  * <pre>
  * http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
  * </pre>
  *
  * @param http the {@link HttpSecurity} to modify
  * @throws Exception if an error occurs
  */
 @Override
 protected void configure(HttpSecurity http) throws Exception {
  http
    .csrf().disable()
    .authorizeRequests().anyRequest().authenticated();
 }
}

1.4.5 測試

訪問http://localhost:8090/order/findAll成功打印出order-list,服務(wù)搭建成功。

2 認(rèn)證服務(wù)實(shí)現(xiàn)登錄,頒發(fā)令牌

首先,我們必須知道我們的項(xiàng)目是前后端分離的項(xiàng)目,所以我們不能由后端控制頁面跳轉(zhuǎn)了,只能返回json串通知前端登錄成功,然后前端根據(jù)后端返回的信息控制頁面跳轉(zhuǎn)。

2.1 登錄成功或者登錄失敗后的源碼分析

UsernamePasswordAuthenticationFilter中登錄成功后走successfulAuthentication方法

/**
	 * Default behaviour for successful authentication.認(rèn)證成功之后的默認(rèn)操作
	 * <ol>
	 * <li>Sets the successful <tt>Authentication</tt> object on the
	 * {@link SecurityContextHolder}</li>
	 * <li>Informs the configured <tt>RememberMeServices</tt> of the successful login</li>
	 * <li>Fires an {@link InteractiveAuthenticationSuccessEvent} via the configured
	 * <tt>ApplicationEventPublisher</tt></li>
	 * <li>Delegates additional behaviour to the {@link AuthenticationSuccessHandler}.</li>
	 * </ol>
	 *
	 * Subclasses can override this method to continue the {@link FilterChain} after
	 * successful authentication.
	 * @param request
	 * @param response
	 * @param chain
	 * @param authResult the object returned from the <tt>attemptAuthentication</tt>
	 * method.
	 * @throws IOException
	 * @throws ServletException
	 */
	protected void successfulAuthentication(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain, Authentication authResult)
			throws IOException, ServletException {

		if (logger.isDebugEnabled()) {
			logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
					+ authResult);
		}

  //將已通過認(rèn)證的Authentication保存到securityContext容器中,應(yīng)為后面的過濾器需要使用
		SecurityContextHolder.getContext().setAuthentication(authResult);

  //記住我
		rememberMeServices.loginSuccess(request, response, authResult);

		// Fire event
		if (this.eventPublisher != null) {
			eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
					authResult, this.getClass()));
		}

  //這個(gè)方法你點(diǎn)進(jìn)去,就會(huì)發(fā)現(xiàn),真正作業(yè)面跳轉(zhuǎn)是在這里
		successHandler.onAuthenticationSuccess(request, response, authResult);
	}

UsernamePasswordAuthenticationFilter中登錄成功后走unsuccessfulAuthentication方法

/**
	 * Default behaviour for unsuccessful authentication.認(rèn)證失敗之后的默認(rèn)操作
	 * <ol>
	 * <li>Clears the {@link SecurityContextHolder}</li>
	 * <li>Stores the exception in the session (if it exists or
	 * <tt>allowSesssionCreation</tt> is set to <tt>true</tt>)</li>
	 * <li>Informs the configured <tt>RememberMeServices</tt> of the failed login</li>
	 * <li>Delegates additional behaviour to the {@link AuthenticationFailureHandler}.</li>
	 * </ol>
	 */
	protected void unsuccessfulAuthentication(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException failed)
			throws IOException, ServletException {
		SecurityContextHolder.clearContext();

		if (logger.isDebugEnabled()) {
			logger.debug("Authentication request failed: " + failed.toString(), failed);
			logger.debug("Updated SecurityContextHolder to contain null Authentication");
			logger.debug("Delegating to authentication failure handler " + failureHandler);
		}

  //記住我失敗
		rememberMeServices.loginFail(request, response);

  //失敗后的頁面跳轉(zhuǎn)都在這里
		failureHandler.onAuthenticationFailure(request, response, failed);
	}

2.2 重寫successfulAuthentication和unsuccessfulAuthentication方法

我們繼承UsernamePasswordAuthenticationFilter這個(gè)過濾器

public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

  /**
  * 這個(gè)方法必須有
  * 在過濾器創(chuàng)建的時(shí)候手動(dòng)將AuthenticationManager對象給這個(gè)過濾器使用
  * @param authenticationManager 這個(gè)對象在自己寫的SecurityConfig里面
  */
 public AuthenticationFilter(AuthenticationManager authenticationManager) {
  super.setAuthenticationManager(authenticationManager);
 }

 /**
  * Default behaviour for successful authentication.認(rèn)證成功之后的默認(rèn)操作
  * @param request
  * @param response
  * @param chain
  * @param authResult the object returned from the <tt>attemptAuthentication</tt>
  *     method.
  * @throws IOException
  * @throws ServletException
  */
 @Override
 protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {

  //認(rèn)證成功的對象放入securityContext容器中
  SecurityContextHolder.getContext().setAuthentication(authResult);

  // Fire event
  if (this.eventPublisher != null) {
   eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
     authResult, this.getClass()));
  }
  //創(chuàng)建令牌
  Map<String, Object> claims=new HashMap<>();
  SysUser sysUser = (SysUser) authResult.getPrincipal();
  claims.put("username",sysUser.getUsername());
  claims.put("authorities",authResult.getAuthorities());
  //這個(gè)方法在下面介紹
  String jwt = JwtUtil.createJwt(claims);
  //直接返回json
  ResponseUtil.responseJson(new Result("200", "登錄成功",jwt),response);
 }


 /**
  * Default behaviour for unsuccessful authentication.
  * @param request
  * @param response
  * @param failed
  */
 @Override
 protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
   //清理容器中保存的認(rèn)證對象
  SecurityContextHolder.clearContext();
  //直接返回json
  ResponseUtil.responseJson(new Result("500", "登錄失敗"),response);

 }
}

2.3 令牌創(chuàng)建

String jwt = JwtUtil.createJwt(claims);

這個(gè)方法干了什么事呢

/**
  * 創(chuàng)建令牌
  * @param claims
  * @return
  */
public static String createJwt(Map<String, Object> claims){
 //獲取私鑰
 String priKey = KeyUtil.readKey("privateKey.txt");
 //將string類型的私鑰轉(zhuǎn)換成PrivateKey,jwt只能接受PrivateKey的私鑰
 PKCS8EncodedKeySpec priPKCS8 = null;
 try {
  priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(priKey));
  KeyFactory keyf = KeyFactory.getInstance("RSA");
  PrivateKey privateKey = keyf.generatePrivate(priPKCS8);
  //創(chuàng)建令牌
  String jws = Jwts.builder()
   //設(shè)置令牌過期時(shí)間30分鐘
   .setExpiration(new Date(System.currentTimeMillis()+1000*60*30))
   //為令牌設(shè)置額外的信息,這里我們設(shè)置用戶名和權(quán)限,還可以根據(jù)需要繼續(xù)添加
   .addClaims(claims)
   //指定加密類型為rsa
   .signWith(privateKey, SignatureAlgorithm.RS256)
   //得到令牌
   .compact();
  log.info("創(chuàng)建令牌成功:"+jws);
  return jws;
 } catch (Exception e) {
  throw new RuntimeException("創(chuàng)建令牌失敗");
 }
}

獲取秘鑰的方法

public class KeyUtil {

 /**
  * 讀取秘鑰
  * @param keyName
  * @return
  */
 public static String readKey(String keyName){
  //文件必須放在resources根目錄下
  ClassPathResource resource=new ClassPathResource(keyName);
  String key =null;
  try {
   InputStream is = resource.getInputStream();
   key = StreamUtils.copyToString(is, Charset.defaultCharset());
  }catch (Exception e){
   throw new RuntimeException("讀取秘鑰錯(cuò)誤");
  }
  if (key==null){
   throw new RuntimeException("秘鑰為空");
  }
  return key;
 }
}

2.4 響應(yīng)json格式數(shù)據(jù)給前端

封裝成了一個(gè)工具類

public class ResponseUtil {

 /**
  * 將結(jié)果以json格式返回
  * @param result 返回結(jié)果
  * @param response
  * @throws IOException
  */
 public static void responseJson(Result result, HttpServletResponse response) throws IOException {
  response.setContentType("application/json;charset=utf-8");
  response.setStatus(200);
  PrintWriter writer = response.getWriter();
  writer.write(JSON.toJSONString(result));
  writer.flush();
  writer.close();
 }
}

返回結(jié)果

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {

 private String code;
 private String msg;
 private Object data;

 public Result(String code, String msg) {
  this.code = code;
  this.msg = msg;
 }
}

3 認(rèn)證服務(wù)實(shí)現(xiàn)令牌驗(yàn)證和解析

除了security配置類中配置的需要忽略的請求之外,其他所有請求必須驗(yàn)證請求頭中是否攜帶令牌,沒有令牌直接響應(yīng)json數(shù)據(jù),否則就驗(yàn)證和解析令牌。

security中有一個(gè)過濾器是實(shí)現(xiàn)令牌BasicAuthenticationFilter認(rèn)證的,只不過他是basic的,沒關(guān)系,我們繼承它,然后重寫解析basic的方法

3.1 源碼分析

@Override
protected void doFilterInternal(HttpServletRequest request,
        HttpServletResponse response, FilterChain chain)
 throws IOException, ServletException {
 final boolean debug = this.logger.isDebugEnabled();

 //獲取請求頭中Authorization的值
 String header = request.getHeader("Authorization");

 if (header == null || !header.toLowerCase().startsWith("basic ")) {
  //值不符合條件直接放行
  chain.doFilter(request, response);
  return;
 }

 try {
  //就是解析Authorization
  String[] tokens = extractAndDecodeHeader(header, request);
  assert tokens.length == 2;

  //tokens[0]用戶名 tokens[1]密碼
  String username = tokens[0];

  if (debug) {
   this.logger
    .debug("Basic Authentication Authorization header found for user '"
      + username + "'");
  }

  //判斷是否需要認(rèn)證(容器中有沒有該認(rèn)證對象)
  if (authenticationIsRequired(username)) {
   //創(chuàng)建一個(gè)對象
   UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
    username, tokens[1]);
   authRequest.setDetails(
    this.authenticationDetailsSource.buildDetails(request));
   //進(jìn)行認(rèn)證,我們不關(guān)心它如何認(rèn)證,我們需要按自己的方法對令牌認(rèn)證解析
   Authentication authResult = this.authenticationManager
    .authenticate(authRequest);

   if (debug) {
    this.logger.debug("Authentication success: " + authResult);
   }

   //已認(rèn)證的對象保存到securityContext中
   SecurityContextHolder.getContext().setAuthentication(authResult);

   //記住我
   this.rememberMeServices.loginSuccess(request, response, authResult);

   onSuccessfulAuthentication(request, response, authResult);
  }

 }
 catch (AuthenticationException failed) {
  SecurityContextHolder.clearContext();

  if (debug) {
   this.logger.debug("Authentication request for failed: " + failed);
  }

  this.rememberMeServices.loginFail(request, response);

  onUnsuccessfulAuthentication(request, response, failed);

  if (this.ignoreFailure) {
   chain.doFilter(request, response);
  }
  else {
   this.authenticationEntryPoint.commence(request, response, failed);
  }

  return;
 }

 chain.doFilter(request, response);
}

3.2 重寫doFilterInternal方法

繼承BasicAuthenticationFilter

public class TokenVerifyFilter extends BasicAuthenticationFilter {


 /**
  * Creates an instance which will authenticate against the supplied
  * {@code AuthenticationManager} and which will ignore failed authentication attempts,
  * allowing the request to proceed down the filter chain.
  * 在過濾器創(chuàng)建的時(shí)候手動(dòng)將AuthenticationManager對象給這個(gè)過濾器使用
  * @param authenticationManager 這個(gè)對象在自己寫的SecurityConfig里面
  */
 public TokenVerifyFilter(AuthenticationManager authenticationManager) {
  super(authenticationManager);
 }

 /**
  * 過濾請求,判斷是否攜帶令牌
  * @param request
  * @param response
  * @param chain
  * @throws IOException
  * @throws ServletException
  */
 @Override
 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

  String header = request.getHeader("Authorization");

  if (header == null || !header.toLowerCase().startsWith("bearer ")) {
   //直接返回json
   ResponseUtil.responseJson(new Result("403", "用戶未登錄"),response);
   return;
  }

  //得到j(luò)wt令牌
  String jwt = StringUtils.replace(header, "bearer ", "");
  //解析令牌
  String[] tokens = JwtUtil.extractAndDecodeJwt(jwt);

  //用戶名
  String username = tokens[0];
  //權(quán)限
  List<SysPermission> authorities= JSON.parseArray(tokens[1], SysPermission.class);

  UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
    username,
    null,
    authorities
    );

  //放入SecurityContext容器中
  SecurityContextHolder.getContext().setAuthentication(authRequest);

  chain.doFilter(request, response);
 }
}

3.3 驗(yàn)證解析令牌

/**
  * 解析令牌
  * @param compactJws
  * @return
  */
public static String decodeJwt(String compactJws){
 //獲取公鑰
 String pubKey = KeyUtil.readKey("publicKey.txt");
 //將string類型的私鑰轉(zhuǎn)換成PublicKey,jwt只能接受PublicKey的公鑰
 KeyFactory keyFactory;
 try {
  X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(
   new BASE64Decoder().decodeBuffer(pubKey));

  keyFactory = KeyFactory.getInstance("RSA");
  PublicKey publicKey = keyFactory.generatePublic(bobPubKeySpec);

  Claims body = Jwts.parserBuilder().setSigningKey(publicKey).build().parseClaimsJws(compactJws).getBody();

  String jwtString = JSON.toJSONString(body);

  //OK, we can trust this JWT
  log.info("解析令牌成功:"+jwtString);
  return jwtString;
 } catch (Exception e) {
  throw new RuntimeException("解析令牌失敗");
 }
}


/**
  * 解析令牌并獲取用戶名和權(quán)限
  * @param compactJws
  * @return String[0]用戶名
  * String[1]權(quán)限
  */
public static String[] extractAndDecodeJwt(String compactJws){
 //獲取令牌的內(nèi)容
 String decodeJwt = decodeJwt(compactJws);
 JSONObject jsonObject = JSON.parseObject(decodeJwt);
 String username = jsonObject.getString("username");
 String authorities = jsonObject.getString("authorities");
 return new String[] { username, authorities };
}

3.4 修改security配置類

將自定義過濾器加入過濾器鏈

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

 @Autowired
 private IUserService iUserService;

 @Autowired
 private BCryptPasswordEncoder bCryptPasswordEncoder;

 /**
  * 只有這個(gè)配置類有AuthenticationManager對象,我們要把這個(gè)類中的這個(gè)對象放入容器中
  * 這樣在別的地方就可以自動(dòng)注入了
  * @return
  * @throws Exception
  */
 @Bean
 @Override
 public AuthenticationManager authenticationManager() throws Exception {
  AuthenticationManager authenticationManager = super.authenticationManagerBean();
  return authenticationManager;
 }

 /**
  * Used by the default implementation of {@link #authenticationManager()} to attempt
  * to obtain an {@link AuthenticationManager}. If overridden, the
  * {@link AuthenticationManagerBuilder} should be used to specify the
  * {@link AuthenticationManager}.
  *
  * <p>
  * The {@link #authenticationManagerBean()} method can be used to expose the resulting
  * {@link AuthenticationManager} as a Bean. The {@link #userDetailsServiceBean()} can
  * be used to expose the last populated {@link UserDetailsService} that is created
  * with the {@link AuthenticationManagerBuilder} as a Bean. The
  * {@link UserDetailsService} will also automatically be populated on
  * {@link HttpSecurity#getSharedObject(Class)} for use with other
  * {@link SecurityContextConfigurer} (i.e. RememberMeConfigurer )
  * </p>
  *
  * <p>
  * For example, the following configuration could be used to register in memory
  * authentication that exposes an in memory {@link UserDetailsService}:
  * </p>
  *
  * <pre>
  * &#064;Override
  * protected void configure(AuthenticationManagerBuilder auth) {
  * 	auth
  * 	// enable in memory based authentication with a user named
  * 	// &quot;user&quot; and &quot;admin&quot;
  * 	.inMemoryAuthentication().withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;).and()
  * 			.withUser(&quot;admin&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;, &quot;ADMIN&quot;);
  * }
  *
  * // Expose the UserDetailsService as a Bean
  * &#064;Bean
  * &#064;Override
  * public UserDetailsService userDetailsServiceBean() throws Exception {
  * 	return super.userDetailsServiceBean();
  * }
  *
  * </pre>
  *
  * @param auth the {@link AuthenticationManagerBuilder} to use
  * @throws Exception
  */
 @Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  //在內(nèi)存中注冊一個(gè)賬號
  //auth.inMemoryAuthentication().withUser("user").password("{noop}123").roles("USER");
  //連接數(shù)據(jù)庫,使用數(shù)據(jù)庫中的賬號
  auth.userDetailsService(iUserService).passwordEncoder(bCryptPasswordEncoder);


 }

 /**
  * Override this method to configure {@link WebSecurity}. For example, if you wish to
  * ignore certain requests.
  *
  * @param web
  */
 @Override
 public void configure(WebSecurity web) throws Exception {
  web.ignoring().antMatchers("/css/**",
    "/img/**",
    "/plugins/**",
    "/favicon.ico",
    "/loginPage");
 }

 /**
  * Override this method to configure the {@link HttpSecurity}. Typically subclasses
  * should not invoke this method by calling super as it may override their
  * configuration. The default configuration is:
  *
  * <pre>
  * http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
  * </pre>
  *
  * @param http the {@link HttpSecurity} to modify
  * @throws Exception if an error occurs
  */
 @Override
 protected void configure(HttpSecurity http) throws Exception {
  http.csrf().disable()
    .httpBasic()
    .and()
    .authorizeRequests()
    .anyRequest().authenticated()
    .and()
    /**
     * 不要將自定義過濾器加component注解,而是在這里直接創(chuàng)建一個(gè)過濾器對象加入到過濾器鏈中,并傳入authenticationManager
     * 啟動(dòng)后,過濾器鏈中會(huì)同時(shí)出現(xiàn)自定義過濾器和他的父類,他會(huì)自動(dòng)覆蓋,并不會(huì)過濾兩次
     *
     * 使用component注解會(huì)產(chǎn)生很多問題:
     * 1. web.ignoring()會(huì)失效,上面的資源還是會(huì)經(jīng)過自定義的過濾器
     * 2.過濾器鏈中出現(xiàn)的是他們父類中的名字
     * 3.登錄的時(shí)候(訪問/login),一直使用匿名訪問,不會(huì)去數(shù)據(jù)庫中查詢
     */
    .addFilterAt(new AuthenticationFilter(super.authenticationManager()), UsernamePasswordAuthenticationFilter.class)
    .addFilterAt(new TokenVerifyFilter(super.authenticationManager()), BasicAuthenticationFilter.class)
    //.formLogin().loginPage("/login.jsp").loginProcessingUrl("/login").defaultSuccessUrl("/index.jsp").failureForwardUrl("/failer.jsp").permitAll()
    .formLogin().loginPage("/loginPage").loginProcessingUrl("/login").permitAll()
    .and()
    .logout().logoutUrl("/logout").logoutSuccessUrl("/loginPage").invalidateHttpSession(true).permitAll();
 }
}

4 資源服務(wù)實(shí)現(xiàn)令牌驗(yàn)證和解析

復(fù)制認(rèn)證服務(wù)的TokenVerifyFilter到資源服務(wù)

然后修改security的配置文件

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 /**
  * Override this method to configure the {@link HttpSecurity}. Typically subclasses
  * should not invoke this method by calling super as it may override their
  * configuration. The default configuration is:
  *
  * <pre>
  * http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
  * </pre>
  *
  * @param http the {@link HttpSecurity} to modify
  * @throws Exception if an error occurs
  */
 @Override
 protected void configure(HttpSecurity http) throws Exception {
  http
    .csrf().disable()
    .authorizeRequests().anyRequest().authenticated()
    .and()
    //禁用session
    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    .and()
   //添加自定義過濾器
    .addFilterAt(new TokenVerifyFilter(super.authenticationManager()), BasicAuthenticationFilter.class);
 }
}

到此這篇關(guān)于spring security在分布式項(xiàng)目下的配置方法(案例詳解)的文章就介紹到這了,更多相關(guān)spring security分布式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot中實(shí)現(xiàn)@Scheduled動(dòng)態(tài)定時(shí)任務(wù)

    SpringBoot中實(shí)現(xiàn)@Scheduled動(dòng)態(tài)定時(shí)任務(wù)

    SpringBoot中的@Scheduled注解為定時(shí)任務(wù)提供了一種很簡單的實(shí)現(xiàn),本文主要介紹了SpringBoot中實(shí)現(xiàn)@Scheduled動(dòng)態(tài)定時(shí)任務(wù),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • 淺談Java之終止繼承:Final類和Fianl方法

    淺談Java之終止繼承:Final類和Fianl方法

    這篇文章主要介紹了Java之終止繼承:Final類和Fianl方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Java實(shí)現(xiàn)KFC點(diǎn)餐系統(tǒng)過程解析

    Java實(shí)現(xiàn)KFC點(diǎn)餐系統(tǒng)過程解析

    這篇文章主要介紹了Java實(shí)現(xiàn)KFC點(diǎn)餐系統(tǒng)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 一文了解為什么Java中只有值傳遞

    一文了解為什么Java中只有值傳遞

    Java?傳參是值傳遞還是引用傳遞?這個(gè)問題很基礎(chǔ),但是許多人都有點(diǎn)懵。本文就來通過一些示例帶大家詳細(xì)了解一下,需要的可以參考一下
    2022-07-07
  • java實(shí)現(xiàn)騰訊ocr圖片識別接口調(diào)用

    java實(shí)現(xiàn)騰訊ocr圖片識別接口調(diào)用

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)騰訊ocr圖片識別接口調(diào)用,拍車牌識別車牌號功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • Java中的Valid和Validated的比較內(nèi)容

    Java中的Valid和Validated的比較內(nèi)容

    在本篇文章里小編給大家整理的是關(guān)于Java中的Valid和Validated的比較內(nèi)容,對此有興趣的朋友們可以學(xué)習(xí)參考下。
    2021-02-02
  • 淺談Spring Boot 微服務(wù)項(xiàng)目的推薦部署方式

    淺談Spring Boot 微服務(wù)項(xiàng)目的推薦部署方式

    這篇文章主要介紹了淺談Spring Boot 微服務(wù)項(xiàng)目的推薦部署方式,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-09-09
  • JAVA?biginteger類bigdecimal類的使用示例學(xué)習(xí)

    JAVA?biginteger類bigdecimal類的使用示例學(xué)習(xí)

    這篇文章主要為大家介紹了JAVA?biginteger類bigdecimal類的使用示例學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • 在Java中輕松將HTML格式文本轉(zhuǎn)換為純文本的方法示例(保留換行)

    在Java中輕松將HTML格式文本轉(zhuǎn)換為純文本的方法示例(保留換行)

    這篇文章主要介紹了在Java中輕松將HTML格式文本轉(zhuǎn)換為純文本的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • Java項(xiàng)目實(shí)現(xiàn)五子棋小游戲

    Java項(xiàng)目實(shí)現(xiàn)五子棋小游戲

    這篇文章主要為大家詳細(xì)介紹了Java項(xiàng)目實(shí)現(xiàn)五子棋小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05

最新評論