spring security集成cas實(shí)現(xiàn)單點(diǎn)登錄過程
cas流程
如下
用戶發(fā)送請(qǐng)求,后臺(tái)服務(wù)器驗(yàn)證ticket(票據(jù)信息),未登錄時(shí),用戶沒有攜帶,所以驗(yàn)證失敗,將用戶重定向到cas服務(wù)器去登錄換取票據(jù)憑證
用戶獲取憑證后攜帶憑證進(jìn)行請(qǐng)求,后臺(tái)服務(wù)器拿著ticket票據(jù)信息去cas服務(wù)器驗(yàn)證,驗(yàn)證成功后并獲取用戶信息,后臺(tái)服務(wù)器再將獲取到的用戶信息返回給瀏覽器
下圖,路線:1->5->6; 1->2->3->4; 1->2->7->8->9->1;

下面代碼解決的問題
1.完成第三方客戶的cas登錄,
2.并將用戶信息同步到自己數(shù)據(jù)庫,
3.同時(shí)將登錄用戶的憑證轉(zhuǎn)換成自己系統(tǒng)識(shí)別的憑證返回給自己系統(tǒng)的前端使用
具體代碼使用
pom.xml
<!-- spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- cas -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
</dependency>
application.yml
security:
cas:
server:
host: https://test-login.xx.com
login: ${security.cas.server.host}/login
logout: ${security.cas.server.host}/logout
service:
webHost: http://test-xx.xx.com
login: /login/cas
logout: /logout
CasServerConfig CAS服務(wù)器配置
@Data
@Configuration
public class CasServerConfig {
@Value("${security.cas.server.host}")
private String host;
@Value("${security.cas.server.login}")
private String login;
@Value("${security.cas.server.logout}")
private String logout;
}
CasServiceConfig 項(xiàng)目后臺(tái)服務(wù)器配置
@Data
@Configuration
public class CasServiceConfig {
@Value("${security.cas.service.webHost}")
private String webHost;
@Value("${security.cas.service.login}")
private String login;
@Value("${security.cas.service.logout}")
private String logout;
private Boolean sendRenew = false;
}
CasWebSecurityConfiguration配置
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
public class CasWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired private CasAuthenticationEntryPoint casAuthenticationEntryPoint;
@Autowired private CasAuthenticationProvider casAuthenticationProvider;
@Autowired private CasAuthenticationFilter casAuthenticationFilter;
@Autowired private LogoutFilter logoutFilter;
@Autowired private CasServerConfig casServerConfig;
@Autowired private HttpParamsFilter httpParamsFilter;
private static final String[] AUTH_WHITELIST = {
// -- swagger ui
"/swagger-resources/**", "/swagger-ui.html", "/v2/api-docs", "/webjars/**", "/v3/api-docs"
};
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
http.csrf().disable();
http.authorizeRequests()
.requestMatchers(CorsUtils::isPreFlightRequest)
.permitAll()
.antMatchers("/static/**")
.permitAll()
.antMatchers(AUTH_WHITELIST)
.permitAll() // 不攔截靜態(tài)資源
// .antMatchers("/config/**").permitAll() // 不攔截動(dòng)態(tài)配置文件
.antMatchers("/api/**")
.permitAll() // 不攔截對(duì)外API
.antMatchers("/login/**")
.permitAll()
.anyRequest()
.authenticated(); // 所有資源都需要登陸后才可以訪問。
http.logout().permitAll(); // 不攔截注銷
http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint);
// 單點(diǎn)注銷的過濾器,必須配置在SpringSecurity的過濾器鏈中,如果直接配置在Web容器中,貌似是不起作用的。我自己的是不起作用的。
SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
singleSignOutFilter.setArtifactParameterName(this.casServerConfig.getHost());
http.addFilter(casAuthenticationFilter)
.addFilterBefore(logoutFilter, LogoutFilter.class)
.addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class);
http.addFilterBefore(httpParamsFilter, FilterSecurityInterceptor.class);
http.antMatcher("/**");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(casAuthenticationProvider);
}
@Bean
public ServletListenerRegistrationBean<CasSignOutHttpSessionListener>
singleSignOutHttpSessionListener() {
ServletListenerRegistrationBean<CasSignOutHttpSessionListener> servletListenerRegistrationBean =
new ServletListenerRegistrationBean<>();
servletListenerRegistrationBean.setListener(new CasSignOutHttpSessionListener());
return servletListenerRegistrationBean;
}
/**
* 自定義UserDetailsService,授權(quán)
*
* @return
*/
@Bean
public CustomUserDetailsService customUserDetailsService() {
return new CustomUserDetailsService();
}
/**
* AuthenticationManager
*
* @return
* @throws Exception
*/
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
@Configuration
public class SecurityConfiguration {
@Autowired private CasServerConfig casServerConfig;
@Autowired private CasServiceConfig casServiceConfig;
@Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(
this.casServiceConfig.getWebHost() + this.casServiceConfig.getLogin());
serviceProperties.setSendRenew(this.casServiceConfig.getSendRenew());
return serviceProperties;
}
@Bean
public CasAuthenticationFilter casAuthenticationFilter(
AuthenticationManager authenticationManager, ServiceProperties serviceProperties) {
CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager);
casAuthenticationFilter.setServiceProperties(serviceProperties);
casAuthenticationFilter.setFilterProcessesUrl(
this.casServiceConfig.getLogin());
casAuthenticationFilter.setContinueChainBeforeSuccessfulAuthentication(false);
casAuthenticationFilter.setAuthenticationSuccessHandler(
new UrlAuthenticationSuccessHandler("/"));
return casAuthenticationFilter;
}
@Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint(
ServiceProperties serviceProperties) {
CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
entryPoint.setLoginUrl(this.casServerConfig.getLogin());
entryPoint.setServiceProperties(serviceProperties);
return entryPoint;
}
@Bean
public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
return new Cas20ServiceTicketValidator(this.casServerConfig.getHost());
}
@Bean
public CasAuthenticationProvider casAuthenticationProvider(
AuthenticationUserDetailsService<CasAssertionAuthenticationToken> userDetailsService,
ServiceProperties serviceProperties,
Cas20ServiceTicketValidator ticketValidator) {
CasAuthenticationProvider provider = new CasAuthenticationProvider();
provider.setKey("casProvider");
provider.setServiceProperties(serviceProperties);
provider.setTicketValidator(ticketValidator);
provider.setAuthenticationUserDetailsService(userDetailsService);
return provider;
}
@Bean
public LogoutFilter logoutFilter() {
String logoutRedirectPath =
this.casServerConfig.getLogout() + "?service=" + this.casServiceConfig.getWebHost();
CasLogoutFilter logoutFilter =
new CasLogoutFilter(logoutRedirectPath, new CasSecurityContextLogoutHandler());
logoutFilter.setFilterProcessesUrl(this.casServiceConfig.getLogout());
return logoutFilter;
}
}
驗(yàn)證成功后處理器UrlAuthenticationSuccessHandler
public class UrlAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
public UrlAuthenticationSuccessHandler() {
super();
}
public UrlAuthenticationSuccessHandler(String defaultTargetUrl) {
super(defaultTargetUrl);
}
@Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
if (isAlwaysUseDefaultTargetUrl()) {
return this.getDefaultTargetUrl();
}
// Check for the parameter and use that if available
String targetUrl = null;
if (this.getTargetUrlParameter() != null) {
targetUrl = request.getParameter(this.getTargetUrlParameter());
if (StringUtils.hasText(targetUrl)) {
logger.debug("Found targetUrlParameter in request: " + targetUrl);
return targetUrl;
}
}
//根據(jù)自己需求寫
if (!StringUtils.hasText(targetUrl)) {
HttpSession session = request.getSession();
targetUrl = (String) session.getAttribute(HttpParamsFilter.REQUESTED_URL);
int firstUrl = targetUrl.indexOf("?url");
if (firstUrl > 0) {
targetUrl = targetUrl.substring(firstUrl + 5);
}
int secondUrl = targetUrl.indexOf("?url");
if (firstUrl >= 0 && secondUrl >= 0 && firstUrl != secondUrl)
targetUrl = targetUrl.substring(secondUrl + 5);
}
if (!StringUtils.hasText(targetUrl)) {
targetUrl = this.getDefaultTargetUrl();
logger.debug("Using default Url: " + targetUrl);
}
//驗(yàn)證成功后,請(qǐng)求后面的ticket信息去除掉
if (targetUrl.contains("ticket")) {
targetUrl = targetUrl.substring(0, targetUrl.indexOf("?ticket"));
}
//根據(jù)自己需求進(jìn)行判斷
if (targetUrl.contains("#")) {
targetUrl += targetUrl.substring(targetUrl.indexOf("#"));
}
return targetUrl;
}
}
退出登錄處理器 CasSecurityContextLogoutHandler
public class CasSecurityContextLogoutHandler implements LogoutHandler {
protected final Log logger = LogFactory.getLog(this.getClass());
private boolean invalidateHttpSession = true;
private boolean clearAuthentication = true;
public CasSecurityContextLogoutHandler() {
}
public void logout(
HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
Assert.notNull(request, "HttpServletRequest required");
if (this.invalidateHttpSession) {
HttpSession session = request.getSession(false);
if (session != null) {
this.logger.debug("Invalidating session: " + session.getId());
//發(fā)布退出登錄事件,自己增加,監(jiān)聽此事件處理一些事情
session.invalidate();
LogoutEvent event = new LogoutEvent();
event.setSessionId(session.getId());
EventPublisherUtil.publish(event);
}
}
if (this.clearAuthentication) {
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication((Authentication)null);
}
SecurityContextHolder.clearContext();
}
public boolean isInvalidateHttpSession() {
return this.invalidateHttpSession;
}
public void setInvalidateHttpSession(boolean invalidateHttpSession) {
this.invalidateHttpSession = invalidateHttpSession;
}
public void setClearAuthentication(boolean clearAuthentication) {
this.clearAuthentication = clearAuthentication;
}
}
增加自己攔截器,處理自己的邏輯HttpParamsFilter
@Component
@Slf4j
public class HttpParamsFilter implements Filter {
public static final String REQUESTED_URL = "CasRequestedUrl";
@Autowired private CasServiceConfig casServiceConfig;
//下面三個(gè)依賴我這里主要做了憑證轉(zhuǎn)換,可刪掉或替換成自己的邏輯
//我自己的服務(wù),可替代成自己的或者刪除掉
@Autowired private TenantTokenService tenantTokenService;
//我自己的服務(wù),可替代成自己的,或者刪除
@Autowired private TenantManager tenantManager;
//我自己的服務(wù),可替代成自己的,或者刪除
@Autowired private UserManager userManager;
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(
ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpSession session = request.getSession();
// String requestPath = request.getServletPath();+ "/" +requestPath
StringBuffer requestURL = request.getRequestURL();
log.info("請(qǐng)求地址:" + requestURL);
Cookie[] cookies = request.getCookies();
boolean flag = true;
if (cookies != null) {
for (Cookie cookie : cookies) {
log.info("cookieDomain:" + cookie.getDomain());
// && requestURL.toString().contains(cookie.getDomain())
if (cookie.getName().contains("token")) {
String value = cookie.getValue();
log.info("token:" + cookie.getDomain());
if (cookie.getDomain() != null && requestURL.toString().contains(cookie.getDomain())) {
//緩存憑證使用的
TenantManager.sessionTokenMap.put(session.getId(), value);
flag = false;
}
}
// && requestURL.toString().contains(cookie.getDomain())
if (cookie.getName().contains("pcpopclub")) {
String value = cookie.getValue();
log.info("pcpopclub:" + cookie.getDomain());
if (cookie.getDomain() != null && requestURL.toString().contains(cookie.getDomain())) {
//緩存憑證使用的
TenantManager.sessionTokenMap.put(session.getId(), value);
flag = false;
}
}
}
}
Map<String, String[]> parameterMap = request.getParameterMap();
for (Entry<String, String[]> params : parameterMap.entrySet()) {
session.setAttribute(params.getKey(), params.getValue());
requestURL.append("?").append(params.getKey()).append("=").append(params.getValue()[0]);
}
//cas服務(wù)器獲取用戶信息
User user = SecurityUtils.getUser();
session.setAttribute(REQUESTED_URL, requestURL.toString());
if (user != null) {
//查詢本地服務(wù)是否有此用戶
AppUser appUser = tenantManager.getUserByUserCode(user.getUser_code());
// 都是自己加的邏輯,不需要可以刪除掉
if (appUser == null) {
// 本地服務(wù)不存在該用戶,調(diào)用遠(yuǎn)程接口查詢用戶信息,并添加到本地服務(wù)
UserQry qry = new UserQry();
qry.setIds(user.getUcid());
UserInfoResponse userBatchInfo = userManager.getUserInfo(qry);
List<UserDTO> data = userBatchInfo.getData();
if (ListUtils.isNotEmpty(data)) {
tenantManager.addUser(BeanUtils.copy(data.get(0), UserDTO.class));
appUser = tenantManager.getUserByUserCode(user.getUser_code());
}
}
String token = TenantManager.sessionTokenMap.get(session.getId());
if (token == null) {
UserTokenVO userTokenVO = new UserTokenVO();
userTokenVO.setPayload(appUser.getId());
SingleResponse<String> generate = tenantTokenService.generate(userTokenVO);
TenantManager.sessionTokenMap.put(session.getId(), generate.getData());
}
} else if (flag) {
session.setAttribute(REQUESTED_URL, casServiceConfig.getWebHost() + requestURL);
}
if (user == null) {
TenantManager.sessionTokenMap.remove(session.getId());
} else {
Cookie ck = new Cookie("homeworld_front_ke_token", user.getBusinessToken());
ck.setDomain(
casServiceConfig
.getWebHost()
.substring(casServiceConfig.getWebHost().indexOf("://") + 3));
response.addCookie(ck);
}
chain.doFilter(request, response);
}
@Override
public void destroy() {}
}
附上獲取用戶的類 SecurityUtils
public class SecurityUtils {
public static Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
public static Collection<? extends GrantedAuthority> getAllPermission() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
return authorities;
}
public static boolean hasPermission(String permission) {
if (StringUtils.isEmpty(permission)) {
return false;
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean hasPermission = false;
for (GrantedAuthority grantedAuthority : authorities) {
String authority = grantedAuthority.getAuthority();
if (authority.equals(permission)) {
hasPermission = true;
}
}
return hasPermission;
}
public static User getUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
if ("anonymousUser".equals(principal)) {
return null;
}
return (User)principal;
}
public static void logout() {
SecurityContextHolder.clearContext();
}
}
附上用戶類 User
@Data
public class User implements UserDetails, CredentialsContainer {
private static final long serialVersionUID = 530L;
private static final Log logger = LogFactory
.getLog(org.springframework.security.core.userdetails.User.class);
private String password;
private String username;
private Set<GrantedAuthority> authorities;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
private boolean enabled;
private String birthday;
private String accountSystemId;
private String gender;
private String displayName;
private String businessToken;
private String avatar;
private String passedMfaReason;
private String version;
private String tgtId;
private String realName;
private String uid;
private String ucname;
private String user_code;
private String phone;
private String mfaAuthMethodLevel;
private String id;
private String state;
private String account;
private String email;
private String ucid;
private String serviceUrl;
public User(){
}
public User(String username, String password,
Collection<? extends GrantedAuthority> authorities) {
this(username, password, true, true, true, true, authorities);
}
public User(String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities) {
if (username != null && !"".equals(username) && password != null) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.accountNonExpired = accountNonExpired;
this.credentialsNonExpired = credentialsNonExpired;
this.accountNonLocked = accountNonLocked;
this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
} else {
throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
}
}
public void setAuthorities(List<GrantedAuthority> grantedAuthorities) {
this.authorities = Collections.unmodifiableSet(sortAuthorities(grantedAuthorities));
}
public Collection<GrantedAuthority> getAuthorities() {
return this.authorities;
}
public String getPassword() {
return this.password;
}
public String getUsername() {
return this.username;
}
public boolean isEnabled() {
return this.enabled;
}
public boolean isAccountNonExpired() {
return this.accountNonExpired;
}
public boolean isAccountNonLocked() {
return this.accountNonLocked;
}
public boolean isCredentialsNonExpired() {
return this.credentialsNonExpired;
}
public void eraseCredentials() {
this.password = null;
}
private static SortedSet<GrantedAuthority> sortAuthorities(
Collection<? extends GrantedAuthority> authorities) {
Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet(new AuthorityComparator());
Iterator var2 = authorities.iterator();
while (var2.hasNext()) {
GrantedAuthority grantedAuthority = (GrantedAuthority) var2.next();
Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements");
sortedAuthorities.add(grantedAuthority);
}
return sortedAuthorities;
}
public boolean equals(Object rhs) {
return rhs instanceof User
? this.username.equals(((User) rhs).username) : false;
}
public int hashCode() {
return this.username.hashCode();
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString()).append(": ");
sb.append("Username: ").append(this.username).append("; ");
sb.append("Password: [PROTECTED]; ");
sb.append("Enabled: ").append(this.enabled).append("; ");
sb.append("AccountNonExpired: ").append(this.accountNonExpired).append("; ");
sb.append("credentialsNonExpired: ").append(this.credentialsNonExpired).append("; ");
sb.append("AccountNonLocked: ").append(this.accountNonLocked).append("; ");
if (!this.authorities.isEmpty()) {
sb.append("Granted Authorities: ");
boolean first = true;
Iterator var3 = this.authorities.iterator();
while (var3.hasNext()) {
GrantedAuthority auth = (GrantedAuthority) var3.next();
if (!first) {
sb.append(",");
}
first = false;
sb.append(auth);
}
} else {
sb.append("Not granted any authorities");
}
return sb.toString();
}
public static UserBuilder withUsername(String username) {
return builder().username(username);
}
public static User.UserBuilder builder() {
return new User.UserBuilder();
}
/**
* @deprecated
*/
@Deprecated
public static User.UserBuilder withDefaultPasswordEncoder() {
logger.warn(
"User.withDefaultPasswordEncoder() is considered unsafe for production and is only intended for sample applications.");
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
User.UserBuilder var10000 = builder();
encoder.getClass();
return var10000.passwordEncoder(encoder::encode);
}
public static User.UserBuilder withUserDetails(UserDetails userDetails) {
return withUsername(userDetails.getUsername()).password(userDetails.getPassword())
.accountExpired(!userDetails.isAccountNonExpired())
.accountLocked(!userDetails.isAccountNonLocked()).authorities(userDetails.getAuthorities())
.credentialsExpired(!userDetails.isCredentialsNonExpired())
.disabled(!userDetails.isEnabled());
}
public static class UserBuilder {
private String username;
private String password;
private List<GrantedAuthority> authorities;
private boolean accountExpired;
private boolean accountLocked;
private boolean credentialsExpired;
private boolean disabled;
private Function<String, String> passwordEncoder;
private UserBuilder() {
this.passwordEncoder = (password) -> {
return password;
};
}
public UserBuilder username(String username) {
Assert.notNull(username, "username cannot be null");
this.username = username;
return this;
}
public UserBuilder password(String password) {
Assert.notNull(password, "password cannot be null");
this.password = password;
return this;
}
public UserBuilder passwordEncoder(Function<String, String> encoder) {
Assert.notNull(encoder, "encoder cannot be null");
this.passwordEncoder = encoder;
return this;
}
public UserBuilder roles(String... roles) {
List<GrantedAuthority> authorities = new ArrayList(roles.length);
String[] var3 = roles;
int var4 = roles.length;
for (int var5 = 0; var5 < var4; ++var5) {
String role = var3[var5];
Assert.isTrue(!role.startsWith("ROLE_"), () -> {
return role + " cannot start with ROLE_ (it is automatically added)";
});
authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
}
return this.authorities((Collection) authorities);
}
public UserBuilder authorities(GrantedAuthority... authorities) {
return this.authorities((Collection) Arrays.asList(authorities));
}
public UserBuilder authorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = new ArrayList(authorities);
return this;
}
public UserBuilder authorities(String... authorities) {
return this.authorities((Collection) AuthorityUtils.createAuthorityList(authorities));
}
public UserBuilder accountExpired(boolean accountExpired) {
this.accountExpired = accountExpired;
return this;
}
public UserBuilder accountLocked(boolean accountLocked) {
this.accountLocked = accountLocked;
return this;
}
public UserBuilder credentialsExpired(boolean credentialsExpired) {
this.credentialsExpired = credentialsExpired;
return this;
}
public UserBuilder disabled(boolean disabled) {
this.disabled = disabled;
return this;
}
public UserDetails build() {
String encodedPassword = (String) this.passwordEncoder.apply(this.password);
return new User(this.username, encodedPassword, !this.disabled, !this.accountExpired,
!this.credentialsExpired, !this.accountLocked, this.authorities);
}
}
private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable {
private static final long serialVersionUID = 530L;
private AuthorityComparator() {
}
public int compare(GrantedAuthority g1, GrantedAuthority g2) {
if (g2.getAuthority() == null) {
return -1;
} else {
return g1.getAuthority() == null ? 1 : g1.getAuthority().compareTo(g2.getAuthority());
}
}
}
}
存憑證的容器
public static Map<String, String> sessionTokenMap = new ConcurrentHashMap<>();
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java服務(wù)限流算法的6種實(shí)現(xiàn)
服務(wù)限流是指通過控制請(qǐng)求的速率或次數(shù)來達(dá)到保護(hù)服務(wù)的目的,本文主要介紹了Java服務(wù)限流算法的6種實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-05-05
java動(dòng)態(tài)代理(jdk與cglib)詳細(xì)解析
靜態(tài)代理:由程序員創(chuàng)建或特定工具自動(dòng)生成源代碼,再對(duì)其編譯。在程序運(yùn)行前,代理類的.class文件就已經(jīng)存在了2013-09-09
非常詳細(xì)的Java異常處理機(jī)制知識(shí)整理大全
Java異常指在程序運(yùn)行時(shí)可能出現(xiàn)的一些錯(cuò)誤,比如試圖打開一個(gè)根本不存在的文件等,異常處理將會(huì)改變程序的控制流程,讓程序有機(jī)會(huì)對(duì)錯(cuò)誤做出處理,下面這篇文章主要給大家介紹了關(guān)于Java異常處理機(jī)制知識(shí)整理的相關(guān)資料,需要的朋友可以參考下2022-11-11
java如何給對(duì)象按照字符串屬性進(jìn)行排序
這篇文章主要介紹了java如何給對(duì)象按照字符串屬性進(jìn)行排序,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
Java常用鎖synchronized和ReentrantLock的區(qū)別
這篇文章主要介紹了Java常用鎖synchronized和ReentrantLock的區(qū)別,二者的功效都是相同的,但又有很多不同點(diǎn),下面我們就進(jìn)入文章了解具體的相關(guān)內(nèi)容吧。需要的小伙伴也可以參考一下2022-05-05
解決spring?data?jpa?saveAll()?保存過慢問題
這篇文章主要介紹了解決spring?data?jpa?saveAll()保存過慢問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
Mybatis Plus 自定義方法實(shí)現(xiàn)分頁功能的示例代碼
這篇文章主要介紹了Mybatis Plus 自定義方法實(shí)現(xiàn)分頁功能的示例代碼,代碼簡單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08

