springsecurity实现自定义SecurityConfigurerAdapter、accessDeniedHandle.authenticationEntryPoint示例
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                springsecurity实现自定义SecurityConfigurerAdapter、accessDeniedHandle.authenticationEntryPoint示例
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.                        
                                文章目錄
- spring properties
- 1.TokenUtil
- 自定義 SecurityConfigurerAdapter
- 自定義corsfilter
- 自定義AuthenticationEntryPoint
- 自定義AccessDeniedHandler
- 匿名訪問注解
- springsecurity配置類
spring properties
@Data @Configuration(proxyBeanMethods = false) @ConfigurationProperties(prefix = "jwt") public class SecurityProperties {/** Request Headers : Authorization */private String header;/** 令牌前綴,最后留個空格 Bearer */private String tokenStartWith;/** 必須使用最少88位的Base64對該令牌進行編碼 */private String base64Secret;private String secret;/** 令牌過期時間 此處單位/毫秒 */private Long tokenValidityInSeconds;/** 在線用戶 key,根據 key 查詢 redis 中在線用戶的數據 */private String onlineKey;/** 驗證碼 key */private String codeKey;public String getTokenStartWith() {return tokenStartWith + " ";} }1.TokenUtil
從token中獲取用戶信息、過期時驗證token等的封裝
@Slf4j @Component public class TokenUtil {@Autowiredprivate SecurityProperties properties;/*** 權限緩存前綴*/private static final String REDIS_PREFIX_AUTH = "auth:";/*** 用戶信息緩存前綴*/private static final String REDIS_PREFIX_USER = "user-details:";/*** redis repository*/@Autowiredprivate RedisUtils redisUtils;/*** 獲取用戶名** @param token Token* @return String*/public String getUsernameFromToken(String token) {Claims claims = getClaimsFromToken(token);return claims != null ? claims.getSubject() : null;}/*** 獲取過期時間** @param token Token* @return Date*/public Date getExpiredFromToken(String token) {Claims claims = getClaimsFromToken(token);return claims != null ? claims.getExpiration() : null;}/*** 獲得 Claims** @param token Token* @return Claims*/private Claims getClaimsFromToken(String token) {Claims claims;try {claims = Jwts.parser().setSigningKey(properties.getSecret()).parseClaimsJws(token).getBody();} catch (Exception e) {log.warn("getClaimsFromToken exception", e);claims = null;}return claims;}/*** 計算過期時間** @return Date*/private Date generateExpired() {return new Date(System.currentTimeMillis() + properties.getTokenValidityInSeconds() * 1000);}/*** 判斷 Token 是否過期** @param token Token* @return Boolean*/private Boolean isTokenExpired(String token) {Date expirationDate = getExpiredFromToken(token);return expirationDate.before(new Date());}/*** 生成 Token** @param userDetails 用戶信息* @return String*/public String generateToken(UserDetails userDetails) {String secret=properties.getSecret();String token = Jwts.builder().setSubject(userDetails.getUsername()).setExpiration(generateExpired()).signWith(SignatureAlgorithm.HS512, secret).compact();String key = REDIS_PREFIX_AUTH + userDetails.getUsername() + ":" + token;redisUtils.set(key, token, properties.getTokenValidityInSeconds() / 1000);putUserDetails(userDetails);return token;}/*** 驗證 Token** @param token Token* @return Boolean*/public Boolean validateToken(String token) {final String username = getUsernameFromToken(token);String key = REDIS_PREFIX_AUTH + username+ ":" + token;Object data = redisUtils.get(key);String redisToken = data == null ? null : data.toString();return StringUtils.isNotEmpty(token) && !isTokenExpired(token) && token.equals(redisToken);}/*** 移除 Token** @param token Token*/public void removeToken(String token) {final String username = getUsernameFromToken(token);String key = REDIS_PREFIX_AUTH + username+ ":" + token;redisUtils.del(key);delUserDetails(username);}/*** 獲得用戶信息 Json 字符串** @param token Token* @return String*/protected String getUserDetailsString(String token) {final String username = getUsernameFromToken(token);String key = REDIS_PREFIX_USER + username;Object data = redisUtils.get(key);return data == null ? null : data.toString();}/*** 獲得用戶信息** @param token Token* @return UserDetails*/public UserDetails getUserDetails(String token) {String userDetailsString = getUserDetailsString(token);if (userDetailsString != null) {return new Gson().fromJson(userDetailsString, JwtUser.class);}return null;}/*** 存儲用戶信息** @param userDetails 用戶信息*/private void putUserDetails(UserDetails userDetails) {String key = REDIS_PREFIX_USER + userDetails.getUsername();redisUtils.set(key, new Gson().toJson(userDetails), properties.getTokenValidityInSeconds() / 1000);}/*** 刪除用戶信息** @param username 用戶名*/private void delUserDetails(String username) {String key = REDIS_PREFIX_USER + username;redisUtils.del(key);}public String getToken(HttpServletRequest request) {final String requestHeader = request.getHeader(properties.getHeader());if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) {return requestHeader.substring(7);}return null;}public static void main(String[] args) {String key = Base64.getEncoder().encodeToString("123".getBytes());Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFwcCIsIndyaXRlIl0sInVpbiI6MSwiZXhwIjoxNTc1MDE1ODgzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiYjdiYjQ1NTQtNTQ4OS00YTg5LWI3NjQtNzNjODI0YzljNGMyIiwiY2xpZW50X2lkIjoibHZoYWliYW8ifQ.x7QZxRAR1wuX_YNLi6EzRJ1iaKr1rIEUgjtYF0oSx5k").getBody();System.out.println(JSON.toJSONString(claims));} }其中生成token的方法將用戶信息也存在了redis中,在 validateToken方法中又從Redis中判斷響應鍵是否存在,從而驗證token‘
自定義 SecurityConfigurerAdapter
public class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {private final TokenUtil tokenUtil;public TokenConfigurer(TokenUtil tokenUtil){this.tokenUtil = tokenUtil;}@Overridepublic void configure(HttpSecurity http) {TokenFilter customFilter = new TokenFilter(tokenUtil);http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);} }自定義corsfilter
@Configuration @EnableWebMvc public class ConfigurerAdapter implements WebMvcConfigurer {@Beanpublic CorsFilter corsFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration config = new CorsConfiguration();// 允許cookies跨域config.setAllowCredentials(true);// #允許向該服務器提交請求的URI,*表示全部允許,在SpringMVC中,如果設成*,會自動轉成當前請求頭中的Originconfig.addAllowedOriginPattern("*");// #允許訪問的頭信息,*表示全部config.addAllowedHeader("*");// 預檢請求的緩存時間(秒),即在這個時間段里,對于相同的跨域請求不會再預檢了config.setMaxAge(18000L);// 允許提交請求的方法類型,*表示全部允許config.addAllowedMethod("OPTIONS");config.addAllowedMethod("HEAD");config.addAllowedMethod("GET");config.addAllowedMethod("PUT");config.addAllowedMethod("POST");config.addAllowedMethod("DELETE");config.addAllowedMethod("PATCH");source.registerCorsConfiguration("/**", config);return new CorsFilter(source);}}自定義AuthenticationEntryPoint
AuthenticationEntryPoint 用來解決匿名用戶訪問無權限資源時的異常
@Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { //拓展AuthenticationEntryPoint接口啟動身份驗證方案。@Overridepublic void commence(HttpServletRequest request,HttpServletResponse response,AuthenticationException authException) throws IOException {// 當用戶嘗試訪問安全的REST資源而不提供任何憑據時,將調用此方法發送401 響應response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException == null ? "Unauthorized" : authException.getMessage());} }自定義AccessDeniedHandler
AccessDeineHandler 用來解決認證過的用戶訪問無權限資源時的異常
@Component public class JwtAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {//當用戶在沒有授權的情況下訪問受保護的REST資源時,將調用此方法發送403 Forbidden響應response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());} }匿名訪問注解
/*** 用于標記匿名訪問方法*/ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AnonymousAccess {}使用:
/*** 訪問首頁提示* @return /*/@GetMapping("/")@AnonymousAccesspublic String index() {return "Backend service started successfully";}springsecurity配置類
@Configuration(proxyBeanMethods = false) @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowiredprivate TokenUtil tokenUtil;@Autowiredprivate CorsFilter corsFilter;@Autowiredprivate JwtAuthenticationEntryPoint authenticationErrorHandler;@Autowiredprivate JwtAccessDeniedHandler jwtAccessDeniedHandler;@Autowiredprivate ApplicationContext applicationContext;@BeanGrantedAuthorityDefaults grantedAuthorityDefaults() {// 去除 ROLE_ 前綴return new GrantedAuthorityDefaults("");}@Beanpublic PasswordEncoder passwordEncoder() {// 密碼加密方式return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {// 搜尋匿名標記 url: @AnonymousAccessMap<RequestMappingInfo, HandlerMethod> handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();Set<String> anonymousUrls = new HashSet<>();for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {HandlerMethod handlerMethod = infoEntry.getValue();AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);if (null != anonymousAccess) { //如果這個方法上面有@ AnonymousAccess則在anonymousUrls集合中加入anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());}}httpSecurity// 禁用 CSRF.csrf().disable().addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)// 授權異常.exceptionHandling().authenticationEntryPoint(authenticationErrorHandler).accessDeniedHandler(jwtAccessDeniedHandler)// 防止iframe 造成跨域.and().headers().frameOptions().disable()// 不創建會話.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 靜態資源等等.antMatchers(HttpMethod.GET,"/*.html","/**/*.html","/**/*.css","/**/*.js","/webSocket/**").permitAll()// swagger 文檔.antMatchers("/swagger-ui.html").permitAll().antMatchers("/swagger-resources/**").permitAll().antMatchers("/webjars/**").permitAll().antMatchers("/*/api-docs").permitAll().antMatchers("/v2/api-docs-ext").permitAll()//.antMatchers("/api/wxmp/**").permitAll()// 文件.antMatchers("/avatar/**").permitAll().antMatchers("/file/**").permitAll()// 阿里巴巴 druid.antMatchers("/druid/**").permitAll().antMatchers("/api/canvas/**").permitAll()// 放行OPTIONS請求.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()// 自定義匿名訪問所有url放行 : 允許匿名和帶權限以及登錄用戶訪問.antMatchers(anonymousUrls.toArray(new String[0])).permitAll()// 所有請求都需要認證.anyRequest().authenticated().and().apply(securityConfigurerAdapter());//將SecurityConfigurerAdapter應用于此SecurityBuilder并調用SecurityConfigurerAdapter.setBuilder(SecurityBuilder) 。}//apply方法:用于進一步自定義的SecurityConfigurerAdapterprivate TokenConfigurer securityConfigurerAdapter() {return new TokenConfigurer(tokenUtil);} }總結
以上是生活随笔為你收集整理的springsecurity实现自定义SecurityConfigurerAdapter、accessDeniedHandle.authenticationEntryPoint示例的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: rabbitmq+redis在优化秒杀商
- 下一篇: spring整合dubbo服务消费和发现
