SpringSecurity實(shí)現(xiàn)登陸認(rèn)證并返回token方式
一丶SpringSecurity+oauth2(密碼模式)方式進(jìn)行認(rèn)證授權(quán)
1.新增自定義配置類(lèi)
實(shí)現(xiàn)WebSecurityConfigurerAdapter
重寫(xiě)其中的configure(HttpSecurity http)方法
配置登陸頁(yè)面以及登陸請(qǐng)求url等參數(shù)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired(required = false)
private AuthenticationEntryPoint authenticationEntryPoint;
@Resource
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Resource
private LogoutHandler oauthLogoutHandler;
@Autowired
private OpenIdAuthenticationSecurityConfig openIdAuthenticationSecurityConfig;
@Autowired
private MobileAuthenticationSecurityConfig mobileAuthenticationSecurityConfig;
@Autowired
private MobileAuthrnticationCodeSecurityConfig mobileAuthrnticationCodeSecurityConfig;
@Autowired
private PasswordAuthrnticationCodeSecurityConfig passwordAuthrnticationCodeSecurityConfig;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private TenantAuthenticationSecurityConfig tenantAuthenticationSecurityConfig;
@Autowired
private TenantProperties tenantProperties;
/**
* 這一步的配置是必不可少的,否則SpringBoot會(huì)自動(dòng)配置一個(gè)AuthenticationManager,覆蓋掉內(nèi)存中的用戶
* @return 認(rèn)證管理對(duì)象
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
//授權(quán)服務(wù)器關(guān)閉basic認(rèn)證
.permitAll()
.and()
.logout()
.logoutUrl(SecurityConstants.LOGOUT_URL)
.logoutSuccessHandler(new OauthLogoutSuccessHandler())
.addLogoutHandler(oauthLogoutHandler)
.clearAuthentication(true)
.and()
.apply(openIdAuthenticationSecurityConfig)
.and()
.apply(mobileAuthenticationSecurityConfig)
.and()
.apply(passwordAuthrnticationCodeSecurityConfig)
.and()
.apply(mobileAuthrnticationCodeSecurityConfig)
.and()
.addFilterBefore(new LoginProcessSetTenantFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf().disable()
// 解決不允許顯示在iframe的問(wèn)題
.headers().frameOptions().disable().cacheControl();
http.formLogin()
.loginPage(SecurityConstants.LOGIN_PAGE)
.loginProcessingUrl(SecurityConstants.OAUTH_LOGIN_PRO_URL)
.successHandler(authenticationSuccessHandler);
// 基于密碼 等模式可以無(wú)session,不支持授權(quán)碼模式
if (authenticationEntryPoint != null) {
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
} else {
// 授權(quán)碼模式單獨(dú)處理,需要session的支持,此模式可以支持所有oauth2的認(rèn)證
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
}
}
}2.OAuth2 授權(quán)服務(wù)器配置
@Configuration
@EnableAuthorizationServer
@AutoConfigureAfter(AuthorizationServerEndpointsConfigurer.class)
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
/**
* 注入authenticationManager 來(lái)支持 password grant type
*/
@Autowired
private AuthenticationManager authenticationManager;
@Resource
private UserDetailsService userDetailsService;
@Autowired
private TokenStore tokenStore;
@Autowired
private WebResponseExceptionTranslator webResponseExceptionTranslator;
@Autowired
private RedisClientDetailsService clientDetailsService;
@Autowired
private RandomValueAuthorizationCodeServices authorizationCodeServices;
@Autowired
private TokenGranter tokenGranter;
/**
* 配置身份認(rèn)證器,配置認(rèn)證方式,TokenStore,TokenGranter,OAuth2RequestFactory
* @param endpoints
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.authorizationCodeServices(authorizationCodeServices)
.exceptionTranslator(webResponseExceptionTranslator)
.tokenGranter(tokenGranter);
}
/**
* 配置應(yīng)用名稱 應(yīng)用id
* 配置OAuth2的客戶端相關(guān)信息
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
clientDetailsService.loadAllClientToCache();
}
/**
* 對(duì)應(yīng)于配置AuthorizationServer安全認(rèn)證的相關(guān)信息,創(chuàng)建ClientCredentialsTokenEndpointFilter核心過(guò)濾器
* @param security
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security
.tokenKeyAccess("isAuthenticated()")
.checkTokenAccess("permitAll()")
//讓/oauth/token支持client_id以及client_secret作登錄認(rèn)證
.allowFormAuthenticationForClients();
}
}3.前端部分代碼
需要傳入grant_type,deviceId,username,password
例如請(qǐng)求是這樣的:
https://oauth.b.com/oauth/token? grant_type=password& # 授權(quán)方式是"密碼式" username=USERNAME& password=PASSWORD& client_id=CLIENT_ID& client_secret=123123& scope=all
//賬號(hào)密碼登陸
var grant_type = 'password_code';
var deviceId = $("input[name=deviceId]").val();
var validCode = $("input[name=validCode]").val();
layer.load(2);
var clients = $("#clients").attr("value");
var loginData ={"clients":clients,"grant_type":grant_type,"username":username,"password":hex_md5(password) ,"deviceId":deviceId,"validCode":validCode}
config.putApp(clients);
$.ajax({
url: config.base_server + '/oauth/token',
xhrFields: {
withCredentials: true
},
data: loginData,
type: 'POST',
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', 'Basic ' + window.btoa(config.clientId + ":" + config.clientSecret));
},
success: function (data) {
if (data.resp_code === 0) {
config.putToken(data.datas);
layer.msg('登錄成功', {icon: 1, time: 500}, function () {
location.replace('./');
});
} else {
layer.closeAll('loading');
layer.msg(data.resp_msg, {icon: 5, time: 500});
}
},
error: function (xhr) {
layer.closeAll('loading');
//區(qū)分錯(cuò)誤信息
//驗(yàn)證碼錯(cuò)誤
if(xhr.responseJSON.error === 'invalid_grant'){
layer.msg(xhr.responseJSON.resp_msg, {icon: 5, time: 500});
}else if(xhr.responseJSON.error === 'unsupported_response_type'){
//賬號(hào)錯(cuò)誤
var win = layer.open({
content:'<div>該賬號(hào)已經(jīng)被系統(tǒng)鎖定或者禁用,<br>如需幫助請(qǐng)及時(shí)聯(lián)系系統(tǒng)管理員進(jìn)行處理!</div>'
,btn: ['確定']
,btnAlign: 'c'
,closeBtn: 0
,yes: function(index, layero){
layer.close(win);
}
});
}else {
layer.msg(xhr.responseJSON.resp_msg, {icon: 5, time: 500});
}
var src = $(".login-code").attr("src");
$(".login-code").attr("src", src + '?t=' + (new Date).getTime());
}
});
//阻止表單跳轉(zhuǎn)
return false;4.如果需要其他自定義的授權(quán)模式
可以新增一個(gè)配置類(lèi)。
@Configuration
public class TokenGranterConfig {
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private TokenStore tokenStore;
@Autowired(required = false)
private List<TokenEnhancer> tokenEnhancer;
@Autowired
private IValidateCodeService validateCodeService;
@Autowired
private RandomValueAuthorizationCodeServices authorizationCodeServices;
private boolean reuseRefreshToken = true;
private AuthorizationServerTokenServices tokenServices;
private TokenGranter tokenGranter;
@Autowired
private UserService userService;
/**
* 是否登錄同應(yīng)用同賬號(hào)互踢
*/
@Value("${zlt.uaa.isSingleLogin:false}")
private boolean isSingleLogin;
/**
* 授權(quán)模式
*/
@Bean
public TokenGranter tokenGranter() {
if (tokenGranter == null) {
tokenGranter = new TokenGranter() {
private CompositeTokenGranter delegate;
@Override
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
if (delegate == null) {
delegate = new CompositeTokenGranter(getAllTokenGranters());
}
return delegate.grant(grantType, tokenRequest);
}
};
}
return tokenGranter;
}
/**
* 所有授權(quán)模式:默認(rèn)的5種模式 + 自定義的模式
*/
private List<TokenGranter> getAllTokenGranters() {
AuthorizationServerTokenServices tokenServices = tokenServices();
AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
OAuth2RequestFactory requestFactory = requestFactory();
//獲取默認(rèn)的授權(quán)模式
List<TokenGranter> tokenGranters = getDefaultTokenGranters(tokenServices, authorizationCodeServices, requestFactory);
if (authenticationManager != null) {
//添加手機(jī)號(hào)加驗(yàn)證碼
tokenGranters.add(new MobileCodeGranter(authenticationManager,tokenServices,clientDetailsService,requestFactory, validateCodeService));
// 添加密碼加圖形驗(yàn)證碼模式
tokenGranters.add(new PwdImgCodeGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory, validateCodeService,userService));
// 添加openId模式
tokenGranters.add(new OpenIdGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory));
// 添加手機(jī)號(hào)加密碼授權(quán)模式
tokenGranters.add(new MobilePwdGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory));
tokenGranters.add(new PwdGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory,userService));
}
return tokenGranters;
}
/**
* 默認(rèn)的授權(quán)模式
*/
private List<TokenGranter> getDefaultTokenGranters(AuthorizationServerTokenServices tokenServices
, AuthorizationCodeServices authorizationCodeServices, OAuth2RequestFactory requestFactory) {
List<TokenGranter> tokenGranters = new ArrayList<>();
// 添加授權(quán)碼模式
tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory));
// 添加刷新令牌的模式
tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, requestFactory));
// 添加隱士授權(quán)模式
tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetailsService, requestFactory));
// 添加客戶端模式
tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory));
if (authenticationManager != null) {
// 添加密碼模式
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory));
}
return tokenGranters;
}
private AuthorizationServerTokenServices tokenServices() {
if (tokenServices != null) {
return tokenServices;
}
this.tokenServices = createDefaultTokenServices();
return tokenServices;
}
private AuthorizationCodeServices authorizationCodeServices() {
if (authorizationCodeServices == null) {
authorizationCodeServices = new InMemoryAuthorizationCodeServices();
}
return authorizationCodeServices;
}
private OAuth2RequestFactory requestFactory() {
return new DefaultOAuth2RequestFactory(clientDetailsService);
}
private DefaultTokenServices createDefaultTokenServices() {
DefaultTokenServices tokenServices = new CustomTokenServices(isSingleLogin);
tokenServices.setTokenStore(tokenStore);
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(reuseRefreshToken);
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setTokenEnhancer(tokenEnhancer());
addUserDetailsService(tokenServices, this.userDetailsService);
return tokenServices;
}
private TokenEnhancer tokenEnhancer() {
if (tokenEnhancer != null) {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(tokenEnhancer);
return tokenEnhancerChain;
}
return null;
}
private void addUserDetailsService(DefaultTokenServices tokenServices, UserDetailsService userDetailsService) {
if (userDetailsService != null) {
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService));
tokenServices.setAuthenticationManager(new ProviderManager(Collections.singletonList(provider)));
}
}
}二丶SpringSecurity+自定義登陸控制器(使用最多)
1.自定義類(lèi)繼承
CustomWebSecurityConfigurerAdapter,配置請(qǐng)求過(guò)濾路徑等
@Configuration
//@EnableWebSecurity
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Autowired
private MemoryCacheSecurityContextRepository memoryCacheSecurityContextRepository;
@Autowired
private CustomAuthenticatedSessionStrategy customAuthenticatedSessionStrategy;
@Autowired
private CustomLogoutHandler customLogoutHandler;
@Autowired
private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
@Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// .requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests()
// .anyRequest().hasRole("ENDPOINT_ADMIN")
// .and()
.authorizeRequests()
.antMatchers("/api/public/**")
.permitAll().and().authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").and()
.authorizeRequests().antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')").and()
.authorizeRequests().anyRequest().authenticated().and().formLogin()
.defaultSuccessUrl("/newweb/templates/admin_grid.html", true)
// .loginPage("/login")
.failureHandler(customAuthenticationFailureHandler).and().logout()
// .logoutUrl("/logout")
// .logoutSuccessUrl("/newweb/templates/admin_login.html")
// .logoutSuccessHandler(new CustomLogoutSuccessHandler())
.invalidateHttpSession(true).addLogoutHandler(customLogoutHandler)
.deleteCookies(SecurityConstants.SECURITY_TOKEN_KEY).and();
// 不創(chuàng)建session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 自定義安全上下文倉(cāng)庫(kù),覆蓋默認(rèn)的httpsession實(shí)現(xiàn)
http.securityContext().securityContextRepository(memoryCacheSecurityContextRepository);
// 認(rèn)證成功之后,不進(jìn)行http session相關(guān)處理
http.sessionManagement().sessionAuthenticationStrategy(customAuthenticatedSessionStrategy);
http.csrf().disable();
//
http.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint);
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
}2.登陸控制器
做一些驗(yàn)證碼處理和賬號(hào)密碼驗(yàn)證等操作
@RestController
@RequestMapping("/api/oauth")
public class LoginController {
@Autowired
private UserService userService;
@Autowired
private TokenEndpoint tokenEndpoint;
@Autowired
private LoginService loginService;
@Autowired
private UserProvider userProvider;
@Autowired
private ConfigValueUtil configValueUtil;
@Autowired
private RedisUtil redisUtil;
@Autowired
private ExpertInfoService expertInfoService;
@Autowired
private RoleService roleService;
@ApiOperation("登陸(切換登錄模式需請(qǐng)清空l(shuí)oginForm中的值)")
@PostMapping("/Login")
public ActionResult<LoginVO> login(Principal principal, @RequestParam Map<String, String> parameters, @RequestBody LoginForm loginForm) throws LoginException {
TenantContextHolder.clear();
UserInfo userInfo = new UserInfo();
String phone = loginForm.getPhone();
String phoneCode = loginForm.getPhoneCode();
String timestampkey = loginForm.getTimestamp();
if(StringUtil.isNotEmpty(phone)){
List<UserEntity> userEntityList = userService.list(new QueryWrapper<UserEntity>().lambda().eq(UserEntity::getMobilePhone,phone));
if(CollectionUtils.isNotEmpty(userEntityList)){
String phoneCode1 = String.valueOf(redisUtil.getString(phone));
if("null".equals(phoneCode1)){
throw new LoginException("驗(yàn)證碼已過(guò)期!");
}
if(!(phoneCode1.equals(phoneCode))){
throw new LoginException("驗(yàn)證碼輸入錯(cuò)誤!");
}
if(StringUtil.isNotEmpty(loginForm.getAccount())){
userEntityList = userEntityList.stream().filter(t->loginForm.getAccount().equals(t.getAccount())).collect(Collectors.toList());
}
if(userEntityList.size() > 1){
List<UserLoginForm> userLoginFormList = JsonUtil.getJsonToList(userEntityList,UserLoginForm.class);
LoginVO loginVO = new LoginVO();
loginVO.setUserLogFormList(userLoginFormList);
return ActionResult.success(loginVO);
}
UserEntity userEntity = userEntityList.get(0);
loginForm.setAccount(userEntity.getAccount());
loginForm.setPassword(userEntity.getPassword());
redisUtil.remove(phone);
}
}else{
String code = loginForm.getCode();
String timestamp = String.valueOf(redisUtil.getString(timestampkey));
if("null".equals(timestamp)){
throw new LoginException("驗(yàn)證碼已過(guò)期!");
}
if(!(code).equalsIgnoreCase(timestamp)){
throw new LoginException("驗(yàn)證碼錯(cuò)誤!");
}
}
loginService.isExistUser(loginForm.getAccount().trim(), loginForm.getPassword().trim());
List<UserEntity> userEntityList = userService.getUserEntitys(StringUtil.isNotEmpty(
loginForm.getPhonePassword())?loginForm.getPhonePassword():loginForm.getAccount());
UserEntity entity = new UserEntity();
if(userEntityList.size() > 1){
for (UserEntity item : userEntityList) {
if(item.getPassword().equals(Md5Util.getStringMd5(loginForm.getPassword() + item.getSecretkey().toLowerCase()))){
if(StringUtil.isNotEmpty(loginForm.getPhonePassword())){
entity = userEntityList.stream().filter(t->loginForm.getAccount().equals(t.getAccount())).collect(Collectors.toList()).get(0);
loginForm.setAccount(entity.getAccount());
loginForm.setPassword(entity.getPassword());
}else{
List<UserLoginForm> userLoginFormList = JsonUtil.getJsonToList(userEntityList,UserLoginForm.class);
LoginVO loginVO = new LoginVO();
loginVO.setUserLogFormList(userLoginFormList);
return ActionResult.success(loginVO);
}
}
}
if(StringUtil.isEmpty(loginForm.getPhonePassword())){
throw new LoginException("賬號(hào)密碼錯(cuò)誤");
}
}
if(StringUtil.isEmpty(loginForm.getPhonePassword())){
entity = userEntityList.get(0);
}
userInfo = loginService.userInfo(userInfo, entity);
// if(StringUtil.isNotEmpty(loginForm.getRoleId())){
// String[] roles = new String[1];
// roles[0] = loginForm.getRoleId();
// userInfo.setRoleIds(roles);
// }
//
// List<RoleLoginVo> roleLoginVoList = new ArrayList<>();
//
// if(ArrayUtils.isNotEmpty(userInfo.getRoleIds())){
// if(userInfo.getRoleIds().length > 1){
// for (String roleId : userInfo.getRoleIds()) {
// RoleLoginVo roleLoginVo = JsonUtil.getJsonToBean(roleService.getById(roleId),RoleLoginVo.class);
// roleLoginVoList.add(roleLoginVo);
// }
// }
// }
// if(CollectionUtil.isNotEmpty(roleLoginVoList)){
// LoginVO loginVO = new LoginVO();
// loginVO.setRoleList(roleLoginVoList);
// return ActionResult.success(loginVO);
// }
userInfo.setMybatisTenantId(entity.getTenantId());
ExpertInfoEntity expertInfoEntity = expertInfoService.getOne(
new QueryWrapper<ExpertInfoEntity>().lambda().eq(ExpertInfoEntity::getUserId,entity.getId()));
if(null != expertInfoEntity){
userInfo.setExpertId(expertInfoEntity.getId());
}
//寫(xiě)入會(huì)話
userProvider.add(userInfo);
//驗(yàn)證賬號(hào)密碼
Map<String, String> map = new HashMap<>(16);
map.put("account",loginForm.getAccount());
map.put("password",loginForm.getPassword());
map.putAll(parameters);
map.put("username", loginForm.getAccount());
OAuth2AccessToken oAuth2AccessToken;
try {
oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, map).getBody();
} catch (HttpRequestMethodNotSupportedException e) {
throw new LoginException("賬號(hào)密碼錯(cuò)誤");
}
TenantContextHolder.setTenant(entity.getTenantId());
//登陸日志記錄在JwtTokenEnhancer類(lèi)中
//獲取主題
LoginVO loginVO = new LoginVO();
loginVO.setToken(oAuth2AccessToken.getTokenType() + " " + oAuth2AccessToken.getValue());
loginVO.setTheme(entity.getTheme() == null ? "classic" : entity.getTheme());
return ActionResult.success(loginVO);
}
}3.前端調(diào)用登陸接口
返回token

總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Springboot使用influxDB時(shí)序數(shù)據(jù)庫(kù)的實(shí)現(xiàn)
項(xiàng)目中需要存放大量設(shè)備日志,且需要對(duì)其進(jìn)行簡(jiǎn)單的數(shù)據(jù)分析,信息提取工作,所以本文就介紹一下Springboot使用influxDB時(shí)序數(shù)據(jù)庫(kù),感興趣的可以了解一下2021-08-08
Java編寫(xiě)簡(jiǎn)單計(jì)算器的完整實(shí)現(xiàn)過(guò)程
這篇文章主要給大家介紹了關(guān)于Java編寫(xiě)簡(jiǎn)單計(jì)算器的完整實(shí)現(xiàn)過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Java中JMM與volatile關(guān)鍵字的學(xué)習(xí)
這篇文章主要介紹了通過(guò)實(shí)例解析JMM和Volatile關(guān)鍵字的學(xué)習(xí),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-09-09
Java流程控制之循環(huán)結(jié)構(gòu)for,增強(qiáng)for循環(huán)
這篇文章主要介紹了Java流程控制之循環(huán)結(jié)構(gòu)for,增強(qiáng)for循環(huán),for循環(huán)是編程語(yǔ)言中一種循環(huán)語(yǔ)句,而循環(huán)語(yǔ)句由循環(huán)體及循環(huán)的判定條件兩部分組成,其表達(dá)式為:for(單次表達(dá)式;條件表達(dá)式;末尾循環(huán)體){中間循環(huán)體;},下面我們倆看看文章內(nèi)容的詳細(xì)介紹2021-12-12
Java之關(guān)于基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型的存放位置
這篇文章主要介紹了Java之關(guān)于基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型的存放位置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
SpringBoot整合Quartz及異步調(diào)用的案例
Quartz是一個(gè)完全由java編寫(xiě)的開(kāi)源作業(yè)調(diào)度框架、它的簡(jiǎn)單易用受到業(yè)內(nèi)人士的一致好評(píng),這篇文章主要介紹了SpringBoot整合Quartz及異步調(diào)用,需要的朋友可以參考下2023-03-03
Java EasyExcel實(shí)現(xiàn)導(dǎo)出多sheet并設(shè)置單元格樣式
EasyExcel是一個(gè)基于Java的、快速、簡(jiǎn)潔、解決大文件內(nèi)存溢出的Excel處理工具,下面我們就來(lái)學(xué)習(xí)一下EasyExcel如何實(shí)現(xiàn)導(dǎo)出多sheet并設(shè)置單元格樣式吧2023-11-11

