實作
新增依賴
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
新增工具類
package com.example.iThomeIronMan.util;
import java.security.Key;
import java.util.Date;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
public class JwtUtil {
private static final long EXPIRATIONTIME = 432_000_000; // 5天
private static final String TOKEN_PREFIX = "Bearer"; // Token前缀
private static final String HEADER_STRING = "Authorization";// 存放Token的Header Key
private static final String KEY = "VincentIsRunningBlogForProgrammingBeginnerSpringSecurityAndJWT"; //給定一組密鑰,用來解密以及加密使用
private static final Key secretKey = Keys.hmacShaKeyFor(KEY.getBytes());
public static void generateToken(HttpServletResponse response, String account) {
Claims claims = Jwts.claims();
claims.put("account", account);
claims.setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME));
String token = Jwts.builder().setClaims(claims).signWith(secretKey).compact();
response.setContentType("application/json");
response.setHeader(HEADER_STRING, token);
}
public static Map<String, Object> parseToken(HttpServletRequest request) {
// 從request的header拿回token
String token = request.getHeader(HEADER_STRING);
if(token != null) {
JwtParser parser = Jwts.parserBuilder().setSigningKey(secretKey).build();
Claims claims = parser.parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
return claims.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
return null;
}
}
修改CustomAuthenticationSuccessHandler
package com.example.iThomeIronMan.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import com.example.iThomeIronMan.util.JwtUtil;
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// TODO Auto-generated method stub
JwtUtil.generateToken(response, authentication.getName());
response.sendRedirect("/information");
}
}
新增JwtAuthenticationFilter
package com.example.iThomeIronMan.filter;
import java.io.IOException;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.filter.GenericFilterBean;
import com.example.iThomeIronMan.util.JwtUtil;
public class JwtAuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest req = (HttpServletRequest) request;
String uri = new String(req.getRequestURI());
// 放行所有靜態檔案
if(uri.contains("/css") || uri.contains("/images") || uri.contains("/js")) {
chain.doFilter(request, response);
return ;
}
if(uri.contains("/login") || uri.contains("/register")) {
chain.doFilter(request, response);
return ;
}
String token = req.getHeader("Authorization");
//驗證token,獲得授權
Map<String, Object> claims = JwtUtil.parseToken(req);
if(claims != null) {
System.err.println(claims);
chain.doFilter(request, response);
}
}
}
修改SecurityConfig
package com.example.iThomeIronMan.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.example.iThomeIronMan.filter.JwtAuthenticationFilter;
import com.example.iThomeIronMan.security.CustomAuthenticationFailureHandler;
import com.example.iThomeIronMan.security.CustomAuthenticationProvider;
import com.example.iThomeIronMan.security.CustomAuthenticationSuccessHandler;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private CustomAuthenticationProvider authProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
auth.authenticationProvider(authProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http.authorizeRequests()
// 設定放行名單
.antMatchers("/login", "/register").permitAll()
// 其餘路徑皆須進行驗證
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").loginProcessingUrl("/login").usernameParameter("account").passwordParameter("password")
.successHandler(new CustomAuthenticationSuccessHandler())
.failureHandler(new CustomAuthenticationFailureHandler())
.and()
.logout().logoutUrl("/logout")
.and()
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
// 關閉CSRF(跨站請求偽造)攻擊的防護,這樣才不會拒絕外部直接對API 發出的請求,例如Postman 與前端
.csrf().disable();
}
@Override
public void configure(WebSecurity web) throws Exception {
// TODO Auto-generated method stub
web.ignoring().antMatchers("/css/**", "/images/**", "/js/**");
}
}