您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“SpringBoot security安全認證登錄如何實現”,內容詳細,步驟清晰,細節處理妥當,希望這篇“SpringBoot security安全認證登錄如何實現”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
時序原圖
pom.xml:
<!-- Spring框架基本的核心工具 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!-- SpringWeb模塊 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <!-- spring security 安全認證 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!--常用工具類 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <!-- JSON工具類 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <!-- 阿里JSON解析器 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency> <!-- io常用工具類 --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> <!-- yml解析器 --> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> </dependency> <!-- Token生成與解析--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> <!-- Jaxb --> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> </dependency> <!-- redis 緩存操作 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- pool 對象池 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!-- 解析客戶端操作系統、瀏覽器等 --> <dependency> <groupId>eu.bitwalker</groupId> <artifactId>UserAgentUtils</artifactId> </dependency> <!-- servlet包 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency>
代碼如下(示例):
RedisCache.java :spring redis 工具組件類,注入RedisTemplate,封裝Redis緩存數據、對象操作。
import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.BoundSetOperations; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Component; /** * spring redis 工具類 * * **/ @SuppressWarnings(value = { "unchecked", "rawtypes" }) @Component public class RedisCache { @Autowired public RedisTemplate redisTemplate; /** * 緩存基本的對象,Integer、String、實體類等 * * @param key 緩存的鍵值 * @param value 緩存的值 */ public <T> void setCacheObject(final String key, final T value) { redisTemplate.opsForValue().set(key, value); } /** * 緩存基本的對象,Integer、String、實體類等 * * @param key 緩存的鍵值 * @param value 緩存的值 * @param timeout 時間 * @param timeUnit 時間顆粒度 */ public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { redisTemplate.opsForValue().set(key, value, timeout, timeUnit); } /** * 設置有效時間 * * @param key Redis鍵 * @param timeout 超時時間 * @return true=設置成功;false=設置失敗 */ public boolean expire(final String key, final long timeout) { return expire(key, timeout, TimeUnit.SECONDS); } /** * 設置有效時間 * * @param key Redis鍵 * @param timeout 超時時間 * @param unit 時間單位 * @return true=設置成功;false=設置失敗 */ public boolean expire(final String key, final long timeout, final TimeUnit unit) { return redisTemplate.expire(key, timeout, unit); } /** * 獲得緩存的基本對象。 * * @param key 緩存鍵值 * @return 緩存鍵值對應的數據 */ public <T> T getCacheObject(final String key) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); return operation.get(key); } /** * 刪除單個對象 * * @param key */ public boolean deleteObject(final String key) { return redisTemplate.delete(key); } /** * 刪除集合對象 * * @param collection 多個對象 * @return */ public long deleteObject(final Collection collection) { return redisTemplate.delete(collection); } /** * 緩存List數據 * * @param key 緩存的鍵值 * @param dataList 待緩存的List數據 * @return 緩存的對象 */ public <T> long setCacheList(final String key, final List<T> dataList) { Long count = redisTemplate.opsForList().rightPushAll(key, dataList); return count == null ? 0 : count; } /** * 獲得緩存的list對象 * * @param key 緩存的鍵值 * @return 緩存鍵值對應的數據 */ public <T> List<T> getCacheList(final String key) { return redisTemplate.opsForList().range(key, 0, -1); } /** * 緩存Set * * @param key 緩存鍵值 * @param dataSet 緩存的數據 * @return 緩存數據的對象 */ public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) { BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key); Iterator<T> it = dataSet.iterator(); while (it.hasNext()) { setOperation.add(it.next()); } return setOperation; } /** * 獲得緩存的set * * @param key * @return */ public <T> Set<T> getCacheSet(final String key) { return redisTemplate.opsForSet().members(key); } /** * 緩存Map * * @param key * @param dataMap */ public <T> void setCacheMap(final String key, final Map<String, T> dataMap) { if (dataMap != null) { redisTemplate.opsForHash().putAll(key, dataMap); } } /** * 獲得緩存的Map * * @param key * @return */ public <T> Map<String, T> getCacheMap(final String key) { return redisTemplate.opsForHash().entries(key); } /** * 往Hash中存入數據 * * @param key Redis鍵 * @param hKey Hash鍵 * @param value 值 */ public <T> void setCacheMapValue(final String key, final String hKey, final T value) { redisTemplate.opsForHash().put(key, hKey, value); } /** * 獲取Hash中的數據 * * @param key Redis鍵 * @param hKey Hash鍵 * @return Hash中的對象 */ public <T> T getCacheMapValue(final String key, final String hKey) { HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash(); return opsForHash.get(key, hKey); } /** * 刪除Hash中的數據 * * @param key * @param mapkey */ public void delCacheMapValue(final String key, final String hkey) { HashOperations hashOperations = redisTemplate.opsForHash(); hashOperations.delete(key, hkey); } /** * 獲取多個Hash中的數據 * * @param key Redis鍵 * @param hKeys Hash鍵集合 * @return Hash對象集合 */ public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) { return redisTemplate.opsForHash().multiGet(key, hKeys); } /** * 獲得緩存的基本對象列表 * * @param pattern 字符串前綴 * @return 對象列表 */ public Collection<String> keys(final String pattern) { return redisTemplate.keys(pattern); } }
RedisConfig:redis配置類。
import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.serializer.StringRedisSerializer; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { @Bean @SuppressWarnings(value = { "unchecked", "rawtypes" }) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); serializer.setObjectMapper(mapper); // 使用StringRedisSerializer來序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(serializer); // Hash的key也采用StringRedisSerializer的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } }
SecurityUtils:安全服務工具類。
import com.ems.mgr.common.constant.HttpStatus; import com.ems.mgr.common.exception.ServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import com.ems.mgr.common.core.domain.model.LoginUser; /** * 安全服務工具類 */ public class SecurityUtils { /** * 用戶ID **/ public static Long getUserId() { try { return getLoginUser().getUserId(); } catch (Exception e) { throw new ServiceException("獲取用戶ID異常", HttpStatus.UNAUTHORIZED); } } /** * 獲取部門ID **/ public static Long getDeptId() { try { return getLoginUser().getDeptId(); } catch (Exception e) { throw new ServiceException("獲取部門ID異常", HttpStatus.UNAUTHORIZED); } } /** * 獲取用戶賬戶 **/ public static String getUsername() { try { return getLoginUser().getUsername(); } catch (Exception e) { throw new ServiceException("獲取用戶賬戶異常", HttpStatus.UNAUTHORIZED); } } /** * 獲取用戶 **/ public static LoginUser getLoginUser() { try { return (LoginUser) getAuthentication().getPrincipal(); } catch (Exception e) { throw new ServiceException("獲取用戶信息異常", HttpStatus.UNAUTHORIZED); } } /** * 獲取Authentication */ public static Authentication getAuthentication() { return SecurityContextHolder.getContext().getAuthentication(); } /** * 生成BCryptPasswordEncoder密碼 * * @param password 密碼 * @return 加密字符串 */ public static String encryptPassword(String password) { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); return passwordEncoder.encode(password); } /** * 判斷密碼是否相同 * * @param rawPassword 真實密碼 * @param encodedPassword 加密后字符 * @return 結果 */ public static boolean matchesPassword(String rawPassword, String encodedPassword) { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); return passwordEncoder.matches(rawPassword, encodedPassword); } /** * 是否為管理員 * * @param userId 用戶ID * @return 結果 */ public static boolean isAdmin(Long userId) { return userId != null && 1L == userId; } }
SecurityConfig:spring security配置。
import com.ems.mgr.framework.security.filter.JwtAuthenticationTokenFilter; import com.ems.mgr.framework.security.handle.AuthenticationEntryPointImpl; import com.ems.mgr.framework.security.handle.LogoutSuccessHandlerImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.web.filter.CorsFilter; /** * spring security配置 */ @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 自定義用戶認證邏輯 */ @Autowired private UserDetailsService userDetailsService; /** * 認證失敗處理類 */ @Autowired private AuthenticationEntryPointImpl unauthorizedHandler; /** * 退出處理類 */ @Autowired private LogoutSuccessHandlerImpl logoutSuccessHandler; /** * token認證過濾器 */ @Autowired private JwtAuthenticationTokenFilter authenticationTokenFilter; /** * 跨域過濾器 */ @Autowired private CorsFilter corsFilter; /** * 解決 無法直接注入 AuthenticationManager * * @return * @throws Exception */ @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /** * anyRequest | 匹配所有請求路徑 * access | SpringEl表達式結果為true時可以訪問 * anonymous | 匿名可以訪問 * denyAll | 用戶不能訪問 * fullyAuthenticated | 用戶完全認證可以訪問(非remember-me下自動登錄) * hasAnyAuthority | 如果有參數,參數表示權限,則其中任何一個權限可以訪問 * hasAnyRole | 如果有參數,參數表示角色,則其中任何一個角色可以訪問 * hasAuthority | 如果有參數,參數表示權限,則其權限可以訪問 * hasIpAddress | 如果有參數,參數表示IP地址,如果用戶IP和參數匹配,則可以訪問 * hasRole | 如果有參數,參數表示角色,則其角色可以訪問 * permitAll | 用戶可以任意訪問 * rememberMe | 允許通過remember-me登錄的用戶訪問 * authenticated | 用戶登錄后可訪問 */ @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity // CSRF禁用,因為不使用session .csrf().disable() // 認證失敗處理類 .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() // 基于token,所以不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // 過濾請求 .authorizeRequests() // 對于登錄login 驗證碼captchaImage 允許匿名訪問 .antMatchers("/ems/login", "/ems/captchaImage","/ems/login/sms","/ems/robot/**/**").anonymous() .antMatchers("/tool/gen/**/**").anonymous() .antMatchers("/message/sendOne").anonymous() .antMatchers( HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**" ).permitAll() .antMatchers("/swagger-ui.html").anonymous() .antMatchers("/swagger-resources/**").anonymous() .antMatchers("/webjars/**").anonymous() .antMatchers("/*/api-docs").anonymous() .antMatchers("/druid/**").anonymous() .antMatchers("/merakWs/**").anonymous() //.antMatchers("/tool/**").anonymous() // 除上面外的所有請求全部需要鑒權認證 .anyRequest().authenticated() .and() .headers().frameOptions().disable(); httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); // 添加JWT filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // 添加CORS filter httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); } /** * 強散列哈希加密實現 */ @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } /** * 身份認證接口 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //設置DaoAuthenticationProvider類PasswordEncoder passwordEncoder auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); } }
spring security配置類配置token認證過濾器(JwtAuthenticationTokenFilter)、
認證失敗處理類(AuthenticationEntryPointImpl)、退出處理類(LogoutSuccessHandlerImpl)
1-JwtAuthenticationTokenFilter:
package com.ems.mgr.framework.security.filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import com.ems.mgr.common.core.domain.model.LoginUser; import com.ems.mgr.common.utils.SecurityUtils; import com.ems.mgr.common.utils.StringUtils; import com.ems.mgr.framework.web.service.TokenService; /** * token過濾器 驗證token有效性 */ @Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Autowired private TokenService tokenService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { LoginUser loginUser = tokenService.getLoginUser(request); if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) { tokenService.verifyToken(loginUser); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } chain.doFilter(request, response); } }
2-AuthenticationEntryPointImpl:認證失敗處理類 返回未授權
package com.ems.mgr.framework.security.handle; import java.io.IOException; import java.io.Serializable; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ems.mgr.common.core.domain.AjaxResult; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSON; import com.ems.mgr.common.constant.HttpStatus; import com.ems.mgr.common.utils.ServletUtils; import com.ems.mgr.common.utils.StringUtils; /** * 認證失敗處理類 返回未授權 * * */ @Component public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException { int code = HttpStatus.UNAUTHORIZED; String msg = StringUtils.format("請求訪問:{},認證失敗,無法訪問系統資源", request.getRequestURI()); ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); } }
3-LogoutSuccessHandlerImpl:自定義退出處理類
package com.ems.mgr.framework.security.handle; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ems.mgr.common.constant.Constants; import com.ems.mgr.common.core.domain.AjaxResult; import com.ems.mgr.framework.manager.factory.AsyncFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import com.alibaba.fastjson.JSON; import com.ems.mgr.common.constant.HttpStatus; import com.ems.mgr.common.core.domain.model.LoginUser; import com.ems.mgr.common.utils.ServletUtils; import com.ems.mgr.common.utils.StringUtils; import com.ems.mgr.framework.manager.AsyncManager; import com.ems.mgr.framework.web.service.TokenService; /** * 自定義退出處理類 返回成功 */ @Configuration public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { @Autowired private TokenService tokenService; /** * 退出處理 * * @return */ @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { LoginUser loginUser = tokenService.getLoginUser(request); if (StringUtils.isNotNull(loginUser)) { String userName = loginUser.getUsername(); // 刪除用戶緩存記錄 tokenService.delLoginUser(loginUser.getToken()); // 記錄用戶退出日志 AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); } ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.SUCCESS, "退出成功"))); } }
讀到這里,這篇“SpringBoot security安全認證登錄如何實現”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。