行展示
主要展示 Spring Security 與 JWT 結合使用構建后端 API 接口。
主要功能包括登陸(如何在 Spring Security 中添加驗證碼登陸),查找,創建,刪除并對用戶權限進行區分等等。
ps:由于只是 Demo,所以沒有調用數據庫,以上所說增刪改查均在 HashMap 中完成。
展示如何使用 Vue 構建前端后與后端的配合,包括跨域的設置,前端登陸攔截
并實現 POST,GET,DELETE 請求。包括如何在 Vue 中使用后端的 XSRF-TOKEN 防范 CSRF 攻擊
實現細節
創建 Spring boot 項目,添加 JJWT 和 Spring Security 的項目依賴,這個非常簡單,有很多的教程都有塊內容,唯一需要注意的是,如果你使用的 Java 版本是 11,那么你還需要添加以下依賴,使用 Java8 則不需要。
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
要使用 Spring Security 實現對用戶的權限控制,首先需要實現一個簡單的 User 對象實現 UserDetails 接口,UserDetails 接口負責提供核心用戶的信息,如果你只需要用戶登陸的賬號密碼,不需要其它信息,如驗證碼等,那么你可以直接使用 Spring Security 默認提供的 User 類,而不需要自己實現。
public class User implements UserDetails {
private String username;
private String password;
private Boolean rememberMe;
private String verifyCode;
private String power;
private Long expirationTime;
private List<GrantedAuthority> authorities;
/**
* 省略其它的 get set 方法
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
User
這個就是我們要使用到的 User 對象,其中包含了 記住我,驗證碼等登陸信息,因為 Spring Security 整合 Jwt 本質上就是用自己自定義的登陸過濾器,去替換 Spring Security 原生的登陸過濾器,這樣的話,原生的記住我功能就會無法使用,所以我在 User 對象里添加了記住我的信息,用來自己實現這個功能。
首先我們來新建一個 TokenAuthenticationHelper 類,用來處理認證過程中的驗證和請求
public class TokenAuthenticationHelper {
/**
* 未設置記住我時 token 過期時間
* */
private static final long EXPIRATION_TIME = 7200000;
/**
* 記住我時 cookie token 過期時間
* */
private static final int COOKIE_EXPIRATION_TIME = 1296000;
private static final String SECRET_KEY = "ThisIsASpringSecurityDemo";
public static final String COOKIE_TOKEN = "COOKIE-TOKEN";
public static final String XSRF = "XSRF-TOKEN";
/**
* 設置登陸成功后令牌返回
* */
public static void addAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException {
// 獲取用戶登陸角色
Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
// 遍歷用戶角色
StringBuffer stringBuffer = new StringBuffer();
authorities.forEach(authority -> {
stringBuffer.append(authority.getAuthority()).append(",");
});
long expirationTime = EXPIRATION_TIME;
int cookExpirationTime = -1;
// 處理登陸附加信息
LoginDetails loginDetails = (LoginDetails) authResult.getDetails();
if (loginDetails.getRememberMe() != null && loginDetails.getRememberMe()) {
expirationTime = COOKIE_EXPIRATION_TIME * 1000;
cookExpirationTime = COOKIE_EXPIRATION_TIME;
}
String jwt = Jwts.builder()
// Subject 設置用戶名
.setSubject(authResult.getName())
// 設置用戶權限
.claim("authorities", stringBuffer)
// 過期時間
.setExpiration(new Date(System.currentTimeMillis() + expirationTime))
// 簽名算法
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
Cookie cookie = new Cookie(COOKIE_TOKEN, jwt);
cookie.setHttpOnly(true);
cookie.setPath("/");
cookie.setMaxAge(cookExpirationTime);
response.addCookie(cookie);
// 向前端寫入數據
LoginResultDetails loginResultDetails = new LoginResultDetails();
ResultDetails resultDetails = new ResultDetails();
resultDetails.setStatus(HttpStatus.OK.value());
resultDetails.setMessage("登陸成功!");
resultDetails.setSuccess(true);
resultDetails.setTimestamp(LocalDateTime.now());
User user = new User();
user.setUsername(authResult.getName());
user.setPower(stringBuffer.toString());
user.setExpirationTime(System.currentTimeMillis() + expirationTime);
loginResultDetails.setResultDetails(resultDetails);
loginResultDetails.setUser(user);
loginResultDetails.setStatus(200);
response.setContentType("application/json; charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(loginResultDetails));
out.flush();
out.close();
}
/**
* 對請求的驗證
* */
public static Authentication getAuthentication(HttpServletRequest request) {
Cookie cookie = WebUtils.getCookie(request, COOKIE_TOKEN);
String token = cookie != null ? cookie.getValue() : null;
if (token != null) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
// 獲取用戶權限
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get("authorities").toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
String userName = claims.getSubject();
if (userName != null) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userName, null, authorities);
usernamePasswordAuthenticationToken.setDetails(claims);
return usernamePasswordAuthenticationToken;
}
return null;
}
return null;
}
}
TokenAuthenticationHelper
至此,我們的基本登陸與驗證所需要的方法就寫完了
ps:其中的 LoginResultDetails 類和 ResultDetails 請看項目源碼,篇幅所限,此處不在贅述。
眾所周知,Spring Security 是借助一系列的 Servlet Filter 來來實現提供各種安全功能的,所以我們要使用 JWT 就需要自己實現兩個和 JWT 有關的過濾器
這兩個過濾器,我們分別來看,先看第一個:
在項目下新建一個包,名為 filter, 在 filter 下新建一個類名為 JwtLoginFilter, 并使其繼承 AbstractAuthenticationProcessingFilter 類,這個類是一個基于瀏覽器的基于 HTTP 的身份驗證請求的抽象處理器。
public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter {
private final VerifyCodeService verifyCodeService;
private final LoginCountService loginCountService;
/**
* @param defaultFilterProcessesUrl 配置要過濾的地址,即登陸地址
* @param authenticationManager 認證管理器,校驗身份時會用到
* @param loginCountService */
public JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager,
VerifyCodeService verifyCodeService, LoginCountService loginCountService) {
super(new AntPathRequestMatcher(defaultFilterProcessesUrl));
this.loginCountService = loginCountService;
// 為 AbstractAuthenticationProcessingFilter 中的屬性賦值
setAuthenticationManager(authenticationManager);
this.verifyCodeService = verifyCodeService;
}
/**
* 提取用戶賬號密碼進行驗證
* */
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
// 判斷是否要拋出 登陸請求過快的異常
loginCountService.judgeLoginCount(httpServletRequest);
// 獲取 User 對象
// readValue 第一個參數 輸入流,第二個參數 要轉換的對象
User user = new ObjectMapper().readValue(httpServletRequest.getInputStream(), User.class);
// 驗證碼驗證
verifyCodeService.verify(httpServletRequest.getSession().getId(), user.getVerifyCode());
// 對 html 標簽進行轉義,防止 XSS 攻擊
String username = user.getUsername();
username = HtmlUtils.htmlEscape(username);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
username,
user.getPassword(),
user.getAuthorities()
);
// 添加驗證的附加信息
// 包括驗證碼信息和是否記住我
token.setDetails(new LoginDetails(user.getRememberMe(), user.getVerifyCode()));
// 進行登陸驗證
return getAuthenticationManager().authenticate(token);
}
/**
* 登陸成功回調
* */
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
loginCountService.cleanLoginCount(request);
// 登陸成功
TokenAuthenticationHelper.addAuthentication(request, response ,authResult);
}
/**
* 登陸失敗回調
* */
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
// 錯誤請求次數加 1
loginCountService.addLoginCount(request, 1);
// 向前端寫入數據
ErrorDetails errorDetails = new ErrorDetails();
errorDetails.setStatus(HttpStatus.UNAUTHORIZED.value());
errorDetails.setMessage("登陸失敗!");
errorDetails.setError(failed.getLocalizedMessage());
errorDetails.setTimestamp(LocalDateTime.now());
errorDetails.setPath(request.getServletPath());
response.setContentType("application/json; charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(errorDetails));
out.flush();
out.close();
}
}
JwtLoginFilter
這個類主要有以下幾個作用
ps:其中的 verifyCodeService 與 loginCountService 方法與本文關系不大,其中的代碼實現請看源碼
唯一需要注意的就是
驗證碼異常需要繼承 AuthenticationException 異常,
可以看到這是一個 Spring Security 各種異常的父類,寫一個驗證碼異常類繼承 AuthenticationException,然后直接將驗證碼異常拋出就好。
以下完整代碼位于 com.bugaugaoshu.security.service.impl.DigitsVerifyCodeServiceImpl 類下
@Override
public void verify(String key, String code) {
String lastVerifyCodeWithTimestamp = verifyCodeRepository.find(key);
// 如果沒有驗證碼,則隨機生成一個
if (lastVerifyCodeWithTimestamp == null) {
lastVerifyCodeWithTimestamp = appendTimestamp(randomDigitString(verifyCodeUtil.getLen()));
}
String[] lastVerifyCodeAndTimestamp = lastVerifyCodeWithTimestamp.split("#");
String lastVerifyCode = lastVerifyCodeAndTimestamp[0];
long timestamp = Long.parseLong(lastVerifyCodeAndTimestamp[1]);
if (timestamp + VERIFY_CODE_EXPIRE_TIMEOUT < System.currentTimeMillis()) {
throw new VerifyFailedException("驗證碼已過期!");
} else if (!Objects.equals(code, lastVerifyCode)) {
throw new VerifyFailedException("驗證碼錯誤!");
}
}
DigitsVerifyCodeServiceImpl
異常代碼在 com.bugaugaoshu.security.exception.VerifyFailedException 類下
第二個用戶過濾器
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
try {
Authentication authentication = TokenAuthenticationHelper.getAuthentication(httpServletRequest);
// 對用 token 獲取到的用戶進行校驗
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(httpServletRequest, httpServletResponse);
} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException |
SignatureException | IllegalArgumentException e) {
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token expired,登陸已過期");
}
}
}
這個就很簡單了,將拿到的用戶 Token 進行解析,如果正確,就將當前用戶加入到 SecurityContext 的上下文中,授予用戶權限,否則返回 Token 過期的異常
接下來我們來配置 Spring Security, 代碼如下
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static String ADMIN = "ROLE_ADMIN";
public static String USER = "ROLE_USER";
private final VerifyCodeService verifyCodeService;
private final LoginCountService loginCountService;
/**
* 開放訪問的請求
*/
private final static String[] PERMIT_ALL_MAPPING = {
"/api/hello",
"/api/login",
"/api/home",
"/api/verifyImage",
"/api/image/verify",
"/images/**"
};
public WebSecurityConfig(VerifyCodeService verifyCodeService, LoginCountService loginCountService) {
this.verifyCodeService = verifyCodeService;
this.loginCountService = loginCountService;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 跨域配置
*/
@Bean
public CorsConfigurationSource corsConfigurationSource() {
// 允許跨域訪問的 URL
List<String> allowedOriginsUrl = new ArrayList<>();
allowedOriginsUrl.add("http://localhost:8080");
allowedOriginsUrl.add("http://127.0.0.1:8080");
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 設置允許跨域訪問的 URL
config.setAllowedOrigins(allowedOriginsUrl);
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(PERMIT_ALL_MAPPING)
.permitAll()
.antMatchers("/api/user/**", "/api/data", "/api/logout")
// USER 和 ADMIN 都可以訪問
.hasAnyAuthority(USER, ADMIN)
.antMatchers("/api/admin/**")
// 只有 ADMIN 才可以訪問
.hasAnyAuthority(ADMIN)
.anyRequest()
.authenticated()
.and()
// 添加過濾器鏈,前一個參數過濾器, 后一個參數過濾器添加的地方
// 登陸過濾器
.addFilterBefore(new JwtLoginFilter("/api/login", authenticationManager(), verifyCodeService, loginCountService), UsernamePasswordAuthenticationFilter.class)
// 請求過濾器
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
// 開啟跨域
.cors()
.and()
// 開啟 csrf
.csrf()
// .disable();
.ignoringAntMatchers(PERMIT_ALL_MAPPING)
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 在內存中寫入用戶數據
auth.
authenticationProvider(daoAuthenticationProvider());
//.inMemoryAuthentication();
// .withUser("user")
// .password(passwordEncoder().encode("123456"))
// .authorities("ROLE_USER")
// .and()
// .withUser("admin")
// .password(passwordEncoder().encode("123456"))
// .authorities("ROLE_ADMIN")
// .and()
// .withUser("block")
// .password(passwordEncoder().encode("123456"))
// .authorities("ROLE_USER")
// .accountLocked(true);
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setHideUserNotFoundExceptions(false);
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsService(new CustomUserDetailsService());
return provider;
}
以上代碼的注釋很詳細,我就不多說了,重點說一下兩個地方一個是 csrf 的問題,另一個就是 inMemoryAuthentication 在內存中寫入用戶的部分。
首先說 csrf 的問題:我看了看網上有很多 Spring Security 的教程,都會將 .csrf()設置為 .disable() , 這種設置雖然方便,但是不夠安全,忽略了使用安全框架的初衷所以為了安全起見,我還是開啟了這個功能,順便學習一下如何使用 XSRF-TOKEN
因為這個項目是一個 Demo, 不涉及數據庫部分,所以我選擇了在內存中直接寫入用戶,網上的向內存中寫入用戶如上代碼注釋部分,這樣寫雖然簡單,但是有一些問題,在打個斷點我們就能知道種方式調用的是 Spring Security 的是 ProviderManager 這個方法,這種方法不方便我們拋出入用戶名不存在或者其異常,它都會拋出 Bad Credentials 異常,不會提示其它錯誤, 如下圖所示。
Spring Security 為了安全考慮,會把所有的登陸異常全部歸結為 Bad Credentials 異常,所以為了能拋出像用戶名不存在的這種異常,如果采用 Spring Security 默認的登陸方式的話, 可以采用像 GitHub 項目 Vhr 里的這種處理方式,但是因為這個項目使用 Jwt 替換掉了默認的登陸方式,想要實現詳細的異常信息拋出就比較復雜了,我找了好久也沒找到比較簡單且合適的方法。如果你有好的方法,歡迎分享。
最后我的解決方案是使用 Spring Security 的 DaoAuthenticationProvider 這個類來成為認證提供者,這個類實現了 AbstractUserDetailsAuthenticationProvider 這一個抽象的用戶詳細信息身份驗證功能,查看注釋我們可以知道 AbstractUserDetailsAuthenticationProvider 提供了 A base AuthenticationProvider that allows subclasses to override and work with UserDetails objects. The class is designed to respond to UsernamePasswordAuthenticationToken authentication requests.(允許子類重寫和使用 UserDetails 對象的基本身份驗證提供程序。該類旨在響應 UsernamePasswordAuthenticationToken 身份驗證請求。)
通過配置自定義的用戶查詢實現類,我們可以直接在 CustomUserDetailsService 里拋出沒有發現用戶名的異常,然后再設置 hideUserNotFoundExceptions 為 false 這樣就可以區別是密碼錯誤,還是用戶名不存在的錯誤了,
但是這種方式還是有一個問題,不能拋出像賬戶被鎖定這種異常,理論上這種功能可以繼承 AbstractUserDetailsAuthenticationProvider 這個抽象類然后自己重寫的登陸方法來實現,我看了看好像比較復雜,一個 Demo 沒必要,我就放棄了。
另外據說安全信息暴露的越少越好,所以暫時就先這樣吧。(算是給自己找個理由)
用戶查找服務
public class CustomUserDetailsService implements UserDetailsService {
private List<UserDetails> userList = new ArrayList<>();
public CustomUserDetailsService() {
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
UserDetails user = User.withUsername("user").password(passwordEncoder.encode("123456")).authorities(WebSecurityConfig.USER).build();
UserDetails admin = User.withUsername("admin").password(passwordEncoder.encode("123456")).authorities(WebSecurityConfig.ADMIN).build();
userList.add(user);
userList.add(admin);
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
for (UserDetails userDetails : userList) {
if (userDetails.getUsername().equals(username)) {
// 此處我嘗試過直接返回 user
// 但是這樣的話,只有后臺服務啟動后第一次登陸會有效
// 推出后第二次登陸會出現 Empty encoded password 的錯誤,導致無法登陸
// 這樣寫就不會出現這種問題了
// 因為在第一次驗證后,用戶的密碼會被清除,導致第二次登陸系統拿到的是空密碼
// 所以需要new一個對象或將原對象復制一份
// 這個解決方案來自 https://stackoverflow.com/questions/43007763/spring-security-encoded-password-gives-me-bad-credentials/43046195#43046195
return new User(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
}
}
throw new UsernameNotFoundException("用戶名不存在,請檢查用戶名或注冊!");
}
}
這部分就比較簡單了,唯一的注意點我在注釋中已經寫的很清楚了,當然你要是使用連接數據庫的話,這個問題就不存在了。
UserDetailsService 這個接口就是 Spring Security 為其它的數據訪問策略做支持的。
至此,一個基本的 Spring Security + JWT 登陸的后端就完成了,你可以寫幾個 controller 然后用 postman 測試功能了。
其它部分的代碼因為比較簡單,你可以參照源碼自行實現你需要的功能。
創建 Vue 項目的方式網上有很多,此處也不再贅述,我只說一點,過去 Vue 項目創建完成后,在項目目錄下會生成一個 config 文件夾,用來存放 vue 的配置,但現在默認創建的項目是不會生成這個文件夾的,需要你手動在項目根目錄下創建 vue.config.js 作為配置文件。
此處請參考:Vue CLI 官方文檔,配置參考部分
附:使用 Vue CIL 創建 Vue 項目
前后端數據傳遞我使用了更為簡單的 fetch api, 當然你也可以選擇兼容性更加好的 axios
Ui 為 ElementUI
為了獲取 XSRF-TOKEN,還需要 VueCookies
最后為了在項目的首頁展示介紹,我還引入了 mavonEditor,一個基于 vue 的 Markdown 插件
引入以上包之后,你與要修改 src 目錄下的 main.js 文件如下。
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import mavonEditor from 'mavon-editor';
import 'mavon-editor/dist/css/index.css';
import VueCookies from 'vue-cookies'
import axios from 'axios'
// 讓ajax攜帶cookie
axios.defaults.withCredentials=true;
// 注冊 axios 為全局變量
Vue.prototype.$axios = axios
// 使用 vue cookie
Vue.use(VueCookies)
Vue.config.productionTip = false
// 使用 ElementUI 組件
Vue.use(ElementUI)
// markdown 解析編輯工具
Vue.use(mavonEditor)
// 后臺服務地址
Vue.prototype.SERVER_API_URL = "http://127.0.0.1:8088/api";
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
在創建 vue.config.js 完成后,你需要在里面輸入以下內容,用來完成 Vue 的跨域配置
module.exports = {
// options...
devServer: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8088',
changeOrigin: true,
ws: true,
pathRewrite:{
'^/api':''
}
}
}
}
}
頁面設計這些沒有什么可寫的了,需要注意的一點就是在對后端服務器進行 POST,DELETE,PUT 等操作時,請在請求頭中帶上 "X-XSRF-TOKEN": this.$cookies.get('XSRF-TOKEN'), 如果不帶,那么哪怕你登陸了,后臺也會返回 403 異常的。
credentials: "include" 這句也不能少,這是攜帶 Cookie 所必須的語句。如果不加這一句,等于沒有攜帶 Cookie,也就等于沒有登陸了。
舉個例子:
deleteItem(data) {
fetch(this.SERVER_API_URL + "/admin/data/" + data.id, {
headers: {
"Content-Type": "application/json; charset=UTF-8",
"X-XSRF-TOKEN": this.$cookies.get('XSRF-TOKEN')
},
method: "DELETE",
credentials: "include"
}).then(response => response.json())
.then(json => {
if (json.status === 200) {
this.systemDataList.splice(data.id, 1);
this.$message({
message: '刪除成功',
type: 'success'
});
} else {
window.console.log(json);
this.$message.error(json.message);
}
});
},
作者:陜西顏值扛把子 來源:知乎 github完整代碼可私信獲取!
人對超過50000個github上的開源java項目做了統計,統計出最常用的16個開源工具類及其方法。
大部分方法可以望文知意,請務必瀏覽一遍,知道有哪些好用的工具類,不必自己造輪子了。
絕對的好東西,直接收藏吧
下面是已經按照使用次數排序的列表,
1. org.apache.commons.io.IOUtils
2. org.apache.commons.io.FileUtils
3. org.apache.commons.lang.StringUtils
4. org.apache.http.util.EntityUtils
5. org.apache.commons.lang3.StringUtils
6. org.apache.commons.io.FilenameUtils
7. org.springframework.util.StringUtils
8. org.apache.commons.lang.ArrayUtils
9. org.apache.commons.lang.StringEscapeUtils
10. org.apache.http.client.utils.URLEncodedUtils
11. org.apache.commons.codec.digest.DigestUtils
12. org.apache.commons.collections.CollectionUtils
13. org.apache.commons.lang3.ArrayUtils
14. org.apache.commons.beanutils.PropertyUtils
15. org.apache.commons.lang3.StringEscapeUtils
16. org.apache.commons.beanutils.BeanUtils
. org.apache.commons.io.IOUtils
closeQuietly:關閉一個IO流、socket、或者selector且不拋出異常,通常放在finally塊 toString:轉換IO流、 Uri、 byte[]為String copy:IO流數據復制,從輸入流寫到輸出流中,最大支持2GB toByteArray:從輸入流、URI獲取byte[] write:把字節. 字符等寫入輸出流 toInputStream:把字符轉換為輸入流 readLines:從輸入流中讀取多行數據,返回List<String> copyLarge:同copy,支持2GB以上數據的復制 lineIterator:從輸入流返回一個迭代器,根據參數要求讀取的數據量,全部讀取,如果數據不夠,則失敗
deleteDirectory:刪除文件夾 readFileToString:以字符形式讀取文件內容 deleteQueitly:刪除文件或文件夾且不會拋出異常 copyFile:復制文件 writeStringToFile:把字符寫到目標文件,如果文件不存在,則創建 forceMkdir:強制創建文件夾,如果該文件夾父級目錄不存在,則創建父級 write:把字符寫到指定文件中 listFiles:列舉某個目錄下的文件(根據過濾器) copyDirectory:復制文件夾 forceDelete:強制刪除文件
isBlank:字符串是否為空 (trim后判斷) isEmpty:字符串是否為空 (不trim并判斷) equals:字符串是否相等 join:合并數組為單一字符串,可傳分隔符 split:分割字符串 EMPTY:返回空字符串 trimToNull:trim后為空字符串則轉換為null replace:替換字符串
toString:把Entity轉換為字符串 consume:確保Entity中的內容全部被消費。可以看到源碼里又一次消費了Entity的內容,假如用戶沒有消費,那調用Entity時候將會把它消費掉 toByteArray:把Entity轉換為字節流 consumeQuietly:和consume一樣,但不拋異常 getContentCharset:獲取內容的編碼
isBlank:字符串是否為空 (trim后判斷) isEmpty:字符串是否為空 (不trim并判斷) equals:字符串是否相等 join:合并數組為單一字符串,可傳分隔符 split:分割字符串 EMPTY:返回空字符串 replace:替換字符串 capitalize:首字符大寫
getExtension:返回文件后綴名 getBaseName:返回文件名,不包含后綴名 getName:返回文件全名 concat:按命令行風格組合文件路徑(詳見方法注釋) removeExtension:刪除后綴名 normalize:使路徑正常化 wildcardMatch:匹配通配符 seperatorToUnix:路徑分隔符改成unix系統格式的,即/ getFullPath:獲取文件路徑,不包括文件名 isExtension:檢查文件后綴名是不是傳入參數(List<String>)中的一個
hasText:檢查字符串中是否包含文本 hasLength:檢測字符串是否長度大于0 isEmpty:檢測字符串是否為空(若傳入為對象,則判斷對象是否為null) commaDelimitedStringToArray:逗號分隔的String轉換為數組 collectionToDelimitedString:把集合轉為CSV格式字符串 replace 替換字符串 7. delimitedListToStringArray:相當于split uncapitalize:首字母小寫 collectionToDelimitedCommaString:把集合轉為CSV格式字符串 tokenizeToStringArray:和split基本一樣,但能自動去掉空白的單詞
contains:是否包含某字符串 addAll:添加整個數組 clone:克隆一個數組 isEmpty:是否空數組 add:向數組添加元素 subarray:截取數組 indexOf:查找某個元素的下標 isEquals:比較數組是否相等 toObject:基礎類型數據數組轉換為對應的Object數組
參考十五:org.apache.commons.lang3.StringEscapeUtils
format:格式化參數,返回一個HTTP POST或者HTTP PUT可用application/x-www-form-urlencoded字符串 parse:把String或者URI等轉換為List<NameValuePair>
md5Hex:MD5加密,返回32位字符串 sha1Hex:SHA-1加密 sha256Hex:SHA-256加密 sha512Hex:SHA-512加密 md5:MD5加密,返回16位字符串
isEmpty:是否為空 select:根據條件篩選集合元素 transform:根據指定方法處理集合元素,類似List的map() filter:過濾元素,雷瑟List的filter() find:基本和select一樣 collect:和transform 差不多一樣,但是返回新數組 forAllDo:調用每個元素的指定方法 isEqualCollection:判斷兩個集合是否一致
contains:是否包含某個字符串 addAll:添加整個數組 clone:克隆一個數組 isEmpty:是否空數組 add:向數組添加元素 subarray:截取數組 indexOf:查找某個元素的下標 isEquals:比較數組是否相等 toObject:基礎類型數據數組轉換為對應的Object數組
getProperty:獲取對象屬性值 setProperty:設置對象屬性值 getPropertyDiscriptor:獲取屬性描述器 isReadable:檢查屬性是否可訪問 copyProperties:復制屬性值,從一個對象到另一個對象 getPropertyDiscriptors:獲取所有屬性描述器 isWriteable:檢查屬性是否可寫 getPropertyType:獲取對象屬性類型
unescapeHtml4:轉義html escapeHtml4:反轉義html escapeXml:轉義xml unescapeXml:反轉義xml escapeJava:轉義unicode編碼 escapeEcmaScript:轉義EcmaScript字符 unescapeJava:反轉義unicode編碼 escapeJson:轉義json字符 escapeXml10:轉義Xml10
這個現在已經廢棄了,建議使用commons-text包里面的方法。
copyPeoperties:復制屬性值,從一個對象到另一個對象 getProperty:獲取對象屬性值 setProperty:設置對象屬性值 populate:根據Map給屬性復制 copyPeoperty:復制單個值,從一個對象到另一個對象 cloneBean:克隆bean實例
感謝閱讀,如果這篇文章幫助了您,歡迎 點贊 ,收藏,關注,轉發 喲。您的幫助是我們前行的動力,我們會提供更多有價值的內容給大家... 謝謝!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。