package com.xinsixian.caiyi.config;
import com.xinsixian.caiyi.JWTAuthenticationEntryPoint;
import com.xinsixian.caiyi.filter.JWTAuthenticationFilter;
import com.xinsixian.caiyi.filter.JWTAuthorizationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
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.EnableWebSecurity;
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.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// @Autowired
// @Qualifier("userDetailsServiceImpl")
/// private UserDetailsService userDetailsService;
/* @Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
// 測試用資源,需要驗證了的用戶才能訪問
.antMatchers("/user/updateUser/**").authenticated()
.antMatchers("/article/likeArticle/**").authenticated()
.antMatchers("/article/likeComment/**").authenticated()
.antMatchers("/article/comment/**").authenticated()
.antMatchers("/article/delComment/**").authenticated()
.antMatchers("/sunticketLikeComment/comment/**").authenticated()
.antMatchers("/sunticketLikeComment/delComment/**").authenticated()
.antMatchers("/sunticketLikeComment/likeComment/**").authenticated()
.antMatchers("/sunticketLikeComment/likeSunticket/**").authenticated()
.antMatchers("/subFans/**").authenticated()
.antMatchers("/sunticket/sunticket/**").authenticated()
.antMatchers("/sunticket/delSunticket/**").authenticated()
//.antMatchers("/cos/**").authenticated()
.antMatchers("/WxPay/buyColorC/**").authenticated()
.antMatchers("/WxPay/withDraw/**").authenticated()
.antMatchers("/find/publishArticle/**").authenticated()
.antMatchers("/find/articleApprove/**").authenticated()
.antMatchers("/find/myBuyArticle/**").authenticated()
.antMatchers("/find/buyArticle/**").authenticated()
.antMatchers("/message/queryMessageList").authenticated()
.antMatchers("/message/countUnreadMessage").authenticated()
// 其他都放行了
.antMatchers("/user/wxLogin").permitAll()
.antMatchers("/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html",
"/webjars/**","/swagger-resources/configuration/ui","/swagger-ui.html").permitAll()
/*.antMatchers("/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html",
"/webjars/**","/swagger-resources/configuration/ui","/swagger-ui.html").permitAll()*/
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// 不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint());
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
filter
package com.xinsixian.caiyi.filter; import com.fasterxml.jackson.databind.ObjectMapper; import com.xinsixian.caiyi.util.token.JwtTokenUtils; import io.jsonwebtoken.MalformedJwtException; import org.apache.log4j.Logger; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class JWTAuthorizationFilter extends BasicAuthenticationFilter { private Logger log = Logger.getLogger(getClass()); public JWTAuthorizationFilter(AuthenticationManager authenticationManager) { super(authenticationManager); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { Object obj = request.getAttribute("userId"); if (obj != null) { Long userId = Long.parseLong((String)obj); log.error("userId == " + userId + " 的用戶被外部強制注入,Ip ==" + request.getLocalAddr()); response.addHeader("err", "not permit ask"); return ; // throw new AccessDeniedException("userId == " + userId + " 的用戶被外部強制注入"); } String tokenHeader = request.getHeader(JwtTokenUtils.TOKEN_HEADER); // 如果請求頭中沒有accessToken信息則直接放行了 if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)) { chain.doFilter(request, response);//接着走security return; } StringBuilder userIdStr = new StringBuilder(""); // try { // 如果請求頭中有token,則進行解析,並且設置認證信息 SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader, userIdStr));//暫不需要權限驗 // getAuthentication(tokenHeader, userId);//簽名認證,一次就好,避免多次認證耗時 request.setAttribute("userId", userIdStr); super.doFilterInternal(request, response, chain); /* } catch (Exception e) { e.printStackTrace(); Map<String, Object> map = new HashMap<>(); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); map.put("code", 401); map.put("msg", e.getMessage()); response.getWriter().write(new ObjectMapper().writeValueAsString(map)); }*/ } // 這裏從token中獲取用戶信息並新建一個token private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader, StringBuilder userIdStr) { String token = tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, ""); userIdStr.append(JwtTokenUtils.getUserId(token).toString()); // String role = JwtTokenUtils.getUserRole(token); if (!userIdStr.equals("")){ ///< 此處爲權限驗證的了,微信端暫時可以忽略 return new UsernamePasswordAuthenticationToken("test", null, Collections.singleton(new SimpleGrantedAuthority("test")) ); } return null; } }
filter
package com.xinsixian.caiyi.filter; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; /** * 不用這麼偷懶, 次類暫時用不到 */ public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private ThreadLocal<Integer> rememberMe = new ThreadLocal<>(); private AuthenticationManager authenticationManager; public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; super.setFilterProcessesUrl("/auth/login"); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { // 從輸入流中獲取到登錄的信息 /* try { LoginUser loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class); rememberMe.set(loginUser.getRememberMe()); return authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList<>()) ); } catch (IOException e) { e.printStackTrace(); return null; }*/ return null; } // 成功驗證後調用的方法 // 如果驗證成功,就生成token並返回 @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { /* JwtUser jwtUser = (JwtUser) authResult.getPrincipal(); System.out.println("jwtUser:" + jwtUser.toString()); boolean isRemember = rememberMe.get() == 1; String role = ""; Collection<? extends GrantedAuthority> authorities = jwtUser.getAuthorities(); for (GrantedAuthority authority : authorities){ role = authority.getAuthority(); } String token = JwtTokenUtils.createToken(jwtUser.getUsername(), role, isRemember); // String token = JwtTokenUtils.createToken(jwtUser.getUsername(), false); // 返回創建成功的token // 但是這裏創建的token只是單純的token // 按照jwt的規定,最後請求的時候應該是 `Bearer token` response.setHeader("token", JwtTokenUtils.TOKEN_PREFIX + token);*/ } @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { response.getWriter().write("authentication failed, reason: " + failed.getMessage()); } }
異常處理
package com.xinsixian.caiyi; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); response.setStatus(HttpServletResponse.SC_FORBIDDEN); //String reason = "AccessToken校驗失敗異常捕獲:"+authException.getMessage(); //String jsonStr = "{code:" + 401 + ", msg:" + reason + "}"; // response.getWriter().write(new ObjectMapper().writeValueAsString(reason)); //response.getWriter().write(new ObjectMapper().writeValueAsString(jsonStr)); Map<String, Object> map = new HashMap<>(); map.put("code", 401); map.put("msg", authException.getMessage() + "need accessToken"); response.getWriter().write(new ObjectMapper().writeValueAsString(map)); } }