spring security在分布式項目下的配置方法(案例詳解)
分布式項目和傳統(tǒng)項目的區(qū)別就是,分布式項目有多個服務,每一個服務僅僅只實現(xiàn)一套系統(tǒng)中一個或幾個功能,所有的服務組合在一起才能實現(xiàn)系統(tǒng)的完整功能。這會產(chǎn)生一個問題,多個服務之間session不能共享,你在其中一個服務中登錄了,登錄信息保存在這個服務的session中,別的服務不知道啊,所以你訪問別的服務還得在重新登錄一次,對用戶十分不友好。為了解決這個問題,于是就產(chǎn)生了單點登錄:
**jwt單點登錄:**就是用戶在登錄服務登錄成功后,登錄服務會產(chǎn)生向前端響應一個token(令牌),以后用戶再訪問系統(tǒng)的資源的時候都要帶上這個令牌,各大服務對這個令牌進行驗證(令牌是否過期,令牌是否被篡改),驗證通過了,可以訪問資源,同時,令牌中也會攜帶一些不重要的信息,比如用戶名,權限。通過解析令牌就能知道當前登錄的用戶和用戶所擁有的權限。
下面我們就來寫一個案例項目看看具體如何使用
1 創(chuàng)建項目結構
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 認證服務security-sever
這個服務僅僅只有兩項功能:
(1)用戶登錄,頒發(fā)令牌
(2)用戶注冊
我們這里只實現(xiàn)第一個功能
1.3.1 認證服務所需的包
<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 導入domain,dao,service,config
這個可以在上篇文檔中找到,我們只需要service中的loadUserByUsername方法及其所調用dao中的方法
完整項目在我的github中,地址:git@github.com:lx972/cloud-security.git
配置文件我們也從上篇中復制過來MvcConfig,SecurityConfig
1.3.4 測試
訪問http://localhost:8080/loginPage成功出現(xiàn)登錄頁面,說明認證服務的骨架搭建成功了
1.4 資源服務security-resource1
實際項目中會有很多資源服務,我只演示一個
為了簡單,資源服務不使用數(shù)據(jù)庫
1.4.1 資源服務所需的包
<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權限的才能訪問
@RestController
@RequestMapping("/order")
public class OrderController {
//@Secured("ORDER_LIST")
@PreAuthorize(value = "hasAuthority('ORDER_LIST')")
@RequestMapping("/findAll")
public String findAll(){
return "order-list";
}
}
擁有PRODUCT_LIST權限的才能訪問
@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
//這個注解先不要加
//@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,服務搭建成功。
2 認證服務實現(xiàn)登錄,頒發(fā)令牌
首先,我們必須知道我們的項目是前后端分離的項目,所以我們不能由后端控制頁面跳轉了,只能返回json串通知前端登錄成功,然后前端根據(jù)后端返回的信息控制頁面跳轉。
2.1 登錄成功或者登錄失敗后的源碼分析
UsernamePasswordAuthenticationFilter中登錄成功后走successfulAuthentication方法
/**
* Default behaviour for successful authentication.認證成功之后的默認操作
* <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);
}
//將已通過認證的Authentication保存到securityContext容器中,應為后面的過濾器需要使用
SecurityContextHolder.getContext().setAuthentication(authResult);
//記住我
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
//這個方法你點進去,就會發(fā)現(xiàn),真正作業(yè)面跳轉是在這里
successHandler.onAuthenticationSuccess(request, response, authResult);
}
UsernamePasswordAuthenticationFilter中登錄成功后走unsuccessfulAuthentication方法
/**
* Default behaviour for unsuccessful authentication.認證失敗之后的默認操作
* <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);
//失敗后的頁面跳轉都在這里
failureHandler.onAuthenticationFailure(request, response, failed);
}
2.2 重寫successfulAuthentication和unsuccessfulAuthentication方法
我們繼承UsernamePasswordAuthenticationFilter這個過濾器
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
/**
* 這個方法必須有
* 在過濾器創(chuàng)建的時候手動將AuthenticationManager對象給這個過濾器使用
* @param authenticationManager 這個對象在自己寫的SecurityConfig里面
*/
public AuthenticationFilter(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
/**
* Default behaviour for successful authentication.認證成功之后的默認操作
* @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 {
//認證成功的對象放入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());
//這個方法在下面介紹
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 {
//清理容器中保存的認證對象
SecurityContextHolder.clearContext();
//直接返回json
ResponseUtil.responseJson(new Result("500", "登錄失敗"),response);
}
}
2.3 令牌創(chuàng)建
String jwt = JwtUtil.createJwt(claims);
這個方法干了什么事呢
/**
* 創(chuàng)建令牌
* @param claims
* @return
*/
public static String createJwt(Map<String, Object> claims){
//獲取私鑰
String priKey = KeyUtil.readKey("privateKey.txt");
//將string類型的私鑰轉換成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()
//設置令牌過期時間30分鐘
.setExpiration(new Date(System.currentTimeMillis()+1000*60*30))
//為令牌設置額外的信息,這里我們設置用戶名和權限,還可以根據(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("讀取秘鑰錯誤");
}
if (key==null){
throw new RuntimeException("秘鑰為空");
}
return key;
}
}
2.4 響應json格式數(shù)據(jù)給前端
封裝成了一個工具類
public class ResponseUtil {
/**
* 將結果以json格式返回
* @param result 返回結果
* @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();
}
}
返回結果
@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 認證服務實現(xiàn)令牌驗證和解析
除了security配置類中配置的需要忽略的請求之外,其他所有請求必須驗證請求頭中是否攜帶令牌,沒有令牌直接響應json數(shù)據(jù),否則就驗證和解析令牌。
security中有一個過濾器是實現(xiàn)令牌BasicAuthenticationFilter認證的,只不過他是basic的,沒關系,我們繼承它,然后重寫解析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 + "'");
}
//判斷是否需要認證(容器中有沒有該認證對象)
if (authenticationIsRequired(username)) {
//創(chuàng)建一個對象
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, tokens[1]);
authRequest.setDetails(
this.authenticationDetailsSource.buildDetails(request));
//進行認證,我們不關心它如何認證,我們需要按自己的方法對令牌認證解析
Authentication authResult = this.authenticationManager
.authenticate(authRequest);
if (debug) {
this.logger.debug("Authentication success: " + authResult);
}
//已認證的對象保存到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)建的時候手動將AuthenticationManager對象給這個過濾器使用
* @param authenticationManager 這個對象在自己寫的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;
}
//得到jwt令牌
String jwt = StringUtils.replace(header, "bearer ", "");
//解析令牌
String[] tokens = JwtUtil.extractAndDecodeJwt(jwt);
//用戶名
String username = tokens[0];
//權限
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 驗證解析令牌
/**
* 解析令牌
* @param compactJws
* @return
*/
public static String decodeJwt(String compactJws){
//獲取公鑰
String pubKey = KeyUtil.readKey("publicKey.txt");
//將string類型的私鑰轉換成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("解析令牌失敗");
}
}
/**
* 解析令牌并獲取用戶名和權限
* @param compactJws
* @return String[0]用戶名
* String[1]權限
*/
public static String[] extractAndDecodeJwt(String compactJws){
//獲取令牌的內容
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;
/**
* 只有這個配置類有AuthenticationManager對象,我們要把這個類中的這個對象放入容器中
* 這樣在別的地方就可以自動注入了
* @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>
* @Override
* protected void configure(AuthenticationManagerBuilder auth) {
* auth
* // enable in memory based authentication with a user named
* // "user" and "admin"
* .inMemoryAuthentication().withUser("user").password("password").roles("USER").and()
* .withUser("admin").password("password").roles("USER", "ADMIN");
* }
*
* // Expose the UserDetailsService as a Bean
* @Bean
* @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 {
//在內存中注冊一個賬號
//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)建一個過濾器對象加入到過濾器鏈中,并傳入authenticationManager
* 啟動后,過濾器鏈中會同時出現(xiàn)自定義過濾器和他的父類,他會自動覆蓋,并不會過濾兩次
*
* 使用component注解會產(chǎn)生很多問題:
* 1. web.ignoring()會失效,上面的資源還是會經(jīng)過自定義的過濾器
* 2.過濾器鏈中出現(xiàn)的是他們父類中的名字
* 3.登錄的時候(訪問/login),一直使用匿名訪問,不會去數(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 資源服務實現(xiàn)令牌驗證和解析
復制認證服務的TokenVerifyFilter到資源服務
然后修改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);
}
}
到此這篇關于spring security在分布式項目下的配置方法(案例詳解)的文章就介紹到這了,更多相關spring security分布式內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot中實現(xiàn)@Scheduled動態(tài)定時任務
SpringBoot中的@Scheduled注解為定時任務提供了一種很簡單的實現(xiàn),本文主要介紹了SpringBoot中實現(xiàn)@Scheduled動態(tài)定時任務,具有一定的參考價值,感興趣的可以了解一下2024-01-01
JAVA?biginteger類bigdecimal類的使用示例學習
這篇文章主要為大家介紹了JAVA?biginteger類bigdecimal類的使用示例學習,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07
在Java中輕松將HTML格式文本轉換為純文本的方法示例(保留換行)
這篇文章主要介紹了在Java中輕松將HTML格式文本轉換為純文本的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04

