1、 JWT工具類編寫
(1)common工程引入依賴(考慮到工具類的通用性)
<!--jjwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
(2)修改common工程,創(chuàng)建util.JwtUtil
package util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Date;
@ConfigurationProperties("jwt.config")
public class JwtUtil {
private String key; //關(guān)鍵字(鹽)
private long ttl; //一個(gè)小時(shí)(過期時(shí)間)
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
/**
* 生成JWT
*
* @param id
* @param subject
* @return
*/
public String createJWT(String id, String subject, String roles) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
JwtBuilder builder = Jwts.builder()
.setId(id) //用戶id
.setSubject(subject) //用戶名
.setIssuedAt(now) //用于設(shè)置簽發(fā)時(shí)間
.signWith(SignatureAlgorithm.HS256, key) //用于設(shè)置簽名秘鑰
.claim("roles", roles); //自定義,設(shè)置用戶角色
if (ttl > 0) {
builder.setExpiration( new Date( nowMillis + ttl)); //設(shè)置過期時(shí)間
}
return builder.compact(); //轉(zhuǎn)化成字符串返回
}
/**
* 解析JWT
* @param jwtStr
* @return
*/
public Claims parseJWT(String jwtStr){
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(jwtStr)
.getBody();
}
}
(3)修改user工程的application.yml, 添加配置
#jwt相關(guān)
jwt:
config:
key: justIT
ttl: 3600000
2、管理員登陸后臺(tái)簽發(fā)token
(1)配置bean,修改user工程Application類
@Bean
public JwtUtil jwtUtil(){
return new JwtUtil();
}
(2)修改AdminController的login方法
@Autowired
private JwtUtil jwtUtil;
/**
* @Description: //TODO 用戶登錄
* @Param: []
* @return: entity.Result
*/
@PostMapping("/login")
public Result login(@RequestBody Admin admin){
admin = adminService.login(admin);
if (admin == null){
return new Result(false, StatusCode.LOGINERROR, "登錄失敗");
}
//使得前后端通話
//生成令牌
Map<String, String> map = new HashMap<>();
String token = jwtUtil.createJWT(admin.getId(), admin.getLoginname(), "admin");
map.put("roles", "admin");
map.put("token", token);
return new Result(true, StatusCode.OK, "登錄成功", map);
}
測(cè)試運(yùn)行結(jié)果:
{
"flag": true,
"code": 20000,
"message": "登陸成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI5ODQzMjc1MDc4ODI5MzgzNjgiLCJzdWIiOiJ4aWFvbWkiLCJpYXQiOjE1MjM1MjQxNTksInJvbGVzIjoiYWRtaW4iLCJleHAiOjE1MjM1MjQ1MTl9._YF3oftRNTbq9WCD8Jg1tqcez3cSWoQiDIxMuPmp73o",
"name":"admin"
}
}
3、刪除用戶功能鑒權(quán)
?(1)需求:刪除用戶,必須擁有管理員權(quán)限,否則不能刪除。
?(2)前后端約定:前端請(qǐng)求微服務(wù)時(shí)需要添加頭信息Authorization,內(nèi)容為Bearer+空格 +token
(1)修改UserServer的deleteById方法 ,判斷請(qǐng)求中的頭信息,提取token并驗(yàn)證權(quán)限。
@Autowired
private HttpServletRequest request;
/**
* 刪除,必須有admin角色
* @param id
*/
public void deleteById(String id) {
//獲取請(qǐng)求頭
String header = request.getHeader("Authorization");
if(header == null && !header.startsWith("Bearer ")){//沒有頭信息,且沒按規(guī)定的格式開頭
throw new RuntimeException("權(quán)限不足");
}
//得到token
String token = header.substring(7);
//對(duì)令牌進(jìn)行驗(yàn)證
try {
Claims claims = jwtUtil.parseJWT(token);
//如果是管理員
if (claims == null && !"admin".equals(claims.get("roles"))){
throw new RuntimeException("權(quán)限不足");
}
}catch (Exception e){
throw new RuntimeException("令牌不正確");
}
userDao.deleteById(id);
}
4、使用攔截器方式實(shí)現(xiàn)token鑒權(quán)
?? 每個(gè)方法都去寫一段代碼,冗余度太高,不利于維護(hù),那如何做使我們的代碼看起來更清爽呢?我們可以將這段代碼放入攔截器去實(shí)現(xiàn)。
4.1、添加攔截器
?? Spring為我們提供了 import org.springframework.web.servlet.HandlerInterceptor 這個(gè)適配器, 實(shí)現(xiàn)此類,可以非常方便的實(shí)現(xiàn)自己的攔截器。他有三個(gè)方法:
分別實(shí)現(xiàn)預(yù)處理、后處理(調(diào)用了Service并返回ModelAndView,但未進(jìn)行頁面渲 染)、返回處理(已經(jīng)渲染了頁面)
- 在preHandle中,可以進(jìn)行編碼、安全控制等處理;
- 在postHandle中,有機(jī)會(huì)修改ModelAndView;
- 在afterCompletion中,可以根據(jù)ex是否為null判斷是否發(fā)生了異常,進(jìn)行日志記錄。
(1)創(chuàng)建攔截器類。創(chuàng)建 com.springboot.user.interceptor.JwtInterceptor
/**
* 攔截器
*/
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("經(jīng)過了攔截器");
return true;
}
(2)配置攔截器類,創(chuàng)建com.Springboot.user.config.InterceptorConfig
/**
* 攔截器配置類
*/
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
private JwtInterceptor jwtInterceptor;
protected void addInterceptors(InterceptorRegistry registry) {
//注冊(cè)攔截器要聲明攔截的對(duì)象和攔截的路徑
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/**/login/**");
}
}
4.2 攔截器驗(yàn)證token
(1)修改攔截器類 JwtInterceptor
/**
*攔截器
*/
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("經(jīng)過了攔截器");
//無論如何都放行,具體能不能操作還是在具體的操作中判斷;
//攔截器只是負(fù)責(zé)把請(qǐng)求頭中有token的令牌進(jìn)行解析驗(yàn)證;
String header = request.getHeader("Authorization");
if(header != null && header.startsWith("Bearer ")){//有頭信息,且按規(guī)定的格式開頭
//得到token
String token = header.substring(7);
//對(duì)令牌進(jìn)行驗(yàn)證
try {
Claims claims = jwtUtil.parseJWT(token);
//如果是管理員
if (claims != null && "admin".equals(claims.get("roles"))){
request.setAttribute("claims_admin", claims);
}
//如果是普通用戶
if (claims != null && "user".equals(claims.get("roles"))){
request.setAttribute("claims_user", claims);
}
}catch (Exception e){
throw new RuntimeException("令牌不正確");
}
}
return true;
}
}
(2)修改UserService的deleteById方法
/**
* 刪除,必須有admin角色
* @param id
*/
public void deleteById(String id) {
Claims claims = (Claims) request.getAttribute("claims_admin");
if (claims == null){
throw new RuntimeException("權(quán)限不足");
}
userDao.deleteById(id);
}
5、發(fā)布信息驗(yàn)證Token
步驟:用戶登陸簽發(fā)JWT -------> 攜帶 token 進(jìn)行相應(yīng)的權(quán)限操作