大概了解下SpringMVC和jwt,百度
代碼:
1.整體框架
2.controller
package com.yiyezhiqiu.jwt.jwt.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPObject;
import com.yiyezhiqiu.jwt.jwt.annotation.LoginAnnotation;
import com.yiyezhiqiu.jwt.jwt.annotation.OtherPermit;
import com.yiyezhiqiu.jwt.jwt.domain.Users;
import com.yiyezhiqiu.jwt.jwt.service.IUsersService;
import com.yiyezhiqiu.jwt.jwt.service.impl.GetToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* 實現jwt,token驗證
* 業務需求:沒有登陸成功,不能訪問其他頁面,當登陸成功后,帶入成功后返回給前端的token來訪問其他頁面
* 實現:
* 1.了解springMVC,mvc在實現時有處理controller,那么我們在處理controller前對路徑進行攔截,實現WebMvcConfigure接口。
* 2.攔截處理:實現HandlerInterceptor接口 ,在實現類中對請求的token進行驗證。
*
*
* 具體做法:
* 1.springboot整合mybatis,跑通能查數據庫
* 2.寫一個攔截處理器,即實現HandlerInterceptor接口
* 2.1:思路
* 對登陸操作不進行token驗證,對非登陸操作要進行token驗證
* 2.2:做法
* 2.2.1:寫兩個注解,一個是用來對登陸進行控制,一個是其他頁面進行控制
* 2.2.2:開始寫MyInterceptor中代碼
*
*
* 3.實現WebMvcConfigure接口,對所有路徑進行攔截,並把攔截器處理器實例加進來-》也就是實現攔截后使用攔截處理器去處理
*
*
*
*
*
*/
@Slf4j
@RestController
@RequestMapping(value = "/users")
public class UsersController {
@Autowired
IUsersService usersService;
@Resource(name = "GetToken")
GetToken getToken;
JSONObject jsonObject = new JSONObject();
/**
* 注冊
*/
@RequestMapping(value = "/register",method = RequestMethod.POST,produces = "application/json")
public JSONObject register(@RequestBody Users users){
int result = usersService.saveUsers(users);
if(result >0){
jsonObject.put("status",true);
jsonObject.put("message","新增成功");
return jsonObject;
}else{
jsonObject.put("status",false);
jsonObject.put("message","新增失敗");
return jsonObject;
}
}
/**
* 登陸
* @param users
* @return
*/
@LoginAnnotation
@RequestMapping(value = "/login",method = RequestMethod.POST,produces = "application/json")
public JSONObject usersLogin(@RequestBody Users users){
log.info("userName;"+users.getUserName()+",password:"+users.getPassword());
int result = usersService.findUsers(users);
if(result >0){
String token = getToken.token(users);
jsonObject.put("users",users);
jsonObject.put("message",token);
}else{
jsonObject.put("message","帳號或者密碼錯誤");
}
return jsonObject;
}
/**
* 其他頁面
* @return
*/
@OtherPermit
@RequestMapping(value = "/otherPage",method = RequestMethod.GET)
public JSONObject otherPage(){
//模仿下登陸后,傳入token,token驗證后返回有效信息
JSONObject jsonObject = new JSONObject();
jsonObject.put("message","驗證成功");
jsonObject.put("code",200);
return jsonObject;
}
}
3.service:
3.1 IUserService
package com.yiyezhiqiu.jwt.jwt.service;
import com.yiyezhiqiu.jwt.jwt.domain.Users;
public interface IUsersService {
public int saveUsers(Users users);
public int findUsers(Users users);
}
3.2 impl下的UsersServiceImpl
package com.yiyezhiqiu.jwt.jwt.service.impl;
import com.yiyezhiqiu.jwt.jwt.dao.UsersDao;
import com.yiyezhiqiu.jwt.jwt.domain.Users;
import com.yiyezhiqiu.jwt.jwt.service.IUsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class UsersServiceImpl implements IUsersService {
@Autowired
UsersDao usersDao;
/**
* 新增
*/
@Override
public int saveUsers(Users users) {
int result = usersDao.saveUsers(users);
return result ;
}
/**
* 查詢
*
*/
@Override
public int findUsers(Users users) {
Map<String,Object> params = new HashMap<>();
params.put("userName",users.getUserName());
params.put("password", users.getPassword());
Users user = usersDao.findUsers(params);
if(null != user){
return 1;
}else{
return 0;
}
}
}
3.3 impl下的GetToken
package com.yiyezhiqiu.jwt.jwt.service.impl;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.yiyezhiqiu.jwt.jwt.domain.Users;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* 生成token
*/
@Component("GetToken")
public class GetToken {
public String token(Users users){
String token = "";
token = JWT.create().withAudience(users.getUserName()).sign(Algorithm.HMAC256(users.getPassword()));
return token;
}
}
4.dao層
package com.yiyezhiqiu.jwt.jwt.dao;
import com.yiyezhiqiu.jwt.jwt.domain.Users;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import java.util.Map;
@Mapper
@Repository
public interface UsersDao {
/**
* 注冊用戶
*/
public int saveUsers(Users users);
/**
* 查詢用戶
*/
public Users findUsers (Map<String,Object> params);
}
5.configure
package com.yiyezhiqiu.jwt.jwt.configure;
import com.yiyezhiqiu.jwt.jwt.interceptor.MyInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyWebMvcConfigure implements WebMvcConfigurer {
/**
* 攔截url並給相應攔截處理器處理
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**")//攔截所有
.excludePathPatterns("/users/register");//不攔截這個注冊路徑
}
}
6.interceptor
package com.yiyezhiqiu.jwt.jwt.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.yiyezhiqiu.jwt.jwt.annotation.LoginAnnotation;
import com.yiyezhiqiu.jwt.jwt.annotation.OtherPermit;
import com.yiyezhiqiu.jwt.jwt.dao.UsersDao;
import com.yiyezhiqiu.jwt.jwt.domain.Users;
import com.yiyezhiqiu.jwt.jwt.service.IUsersService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
public class MyInterceptor implements HandlerInterceptor {
@Autowired
IUsersService usersService;
@Autowired
UsersDao usersDao;
private static MyInterceptor myInterceptor;
/**
* 這一段用來解決依賴注入不成功問題
*/
@PostConstruct
public void init (){
myInterceptor = this;
myInterceptor.usersDao = this.usersDao;
}
/**
* springMVC正常流程時,處理器處理controller前被攔截處理
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
JSONObject jsonObject = new JSONObject();
String token = request.getHeader("token");
//HandleMethod包含一些controller的方法,handler 就是一個全路徑方法
HandlerMethod handlerMethod = (HandlerMethod)handler;
Method method = handlerMethod.getMethod();
//思路,通過判斷請求的路徑中的方法是否有相應的注解
if(method.isAnnotationPresent(LoginAnnotation.class)){
log.info("這是登陸");
return true;
}
//需要驗證token
if(method.isAnnotationPresent(OtherPermit.class)){
//重置reset
response.reset();
//設置編碼格式
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
//這里用reponse.getWriter()后面會報重復使用的錯
ServletOutputStream pw = response.getOutputStream();
log.info("進來驗證token");
if(StringUtils.isEmpty(token)){
jsonObject.put("message","no token");
pw.print(jsonObject.toJSONString());
return false;
}else{
try{
String userName = JWT.decode(token).getAudience().get(0);
log.info("userName;"+userName);
Map<String,Object> map = new HashMap<>();
map.put("userName",userName);
log.info("userDao:"+usersDao);
log.info("userservice:"+usersService);
Users users = myInterceptor.usersDao.findUsers(map);
try{
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(users.getPassword())).build();
jwtVerifier.verify(token);
return true;
}catch (Exception e){
jsonObject.put("message","token invalid");
jsonObject.put("code","401");
pw.print(jsonObject.toJSONString());
return false;
}
}catch(Exception e){
jsonObject.put("message","token invalid");
jsonObject.put("code","401");
pw.print(jsonObject.toJSONString());
return false;
}
}
}
return false;
}
/**
* MVC時處理器處理controller后被攔截處理
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 在返回視圖前攔截
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
7.annotation;
7.1 LoginAnnotation
package com.yiyezhiqiu.jwt.jwt.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 注明是登陸注解
*/
@Target({ElementType.METHOD,ElementType.TYPE}) //注解放在哪,方法上還是類還是哪里
@Retention(RetentionPolicy.RUNTIME)//啥時候啟用,運行時
public @interface LoginAnnotation {
boolean get() default true;
}
7.2 Otherpermit
package com.yiyezhiqiu.jwt.jwt.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 其他頁面注解
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OtherPermit {
boolean other() default true;
}
8.domain
package com.yiyezhiqiu.jwt.jwt.domain;
import lombok.Data;
@Data
public class Users {
private int id;
private String userName;
private String password;
private String gender;
}
9.UsersMapping.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.yiyezhiqiu.jwt.jwt.dao.UsersDao">
<insert id = "saveUsers" parameterType="com.yiyezhiqiu.jwt.jwt.domain.Users" keyProperty="id" useGeneratedKeys="true">
insert into users(user_name,password,gender) values (#{userName},#{password},#{gender})
</insert>
<select id="findUsers" parameterType="java.util.HashMap" resultType="com.yiyezhiqiu.jwt.jwt.domain.Users">
select * from users where
user_name = #{userName}
<if test="password != null and password != ''">
and password = #{password}
</if>
</select>
</mapper>
10. application.yml
#端口號
server:
port: 8088
#數據庫
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
username: root
password: zhang
#dao和xml映射
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.yiyezhiqiu.jwt.jwt.domain
#可以打印出sql語句
logging:
level:
com:
yiyezhiqiu:
jwt:
jwt:
dao: debug
11.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yiyezhiqiu.jwt</groupId>
<artifactId>jwt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>jwt</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--jwt 依賴-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
結果:postman調試
1.登陸測試:輸入用戶名,密碼。而且為正確
2.登陸測試,輸入用戶名和密碼不正確
3. 不帶token,訪問其他頁面
4.帶正確toke訪問其他頁面
5.帶不正確token訪問其他頁面
github地址:https://github.com/yiyeluowuchen/springboot-jwt-token.git
總結:
遇到問題與解決
1.問題:在攔截器 MyInterceptor中以下注入失敗,一直報空。
@Autowired
IUsersService usersService;
@Autowired
UsersDao usersDao;
解決:
/**
* 這一段用來解決依賴注入不成功問題
*/
private static MyInterceptor myInterceptor;
@PostConstruct
public void init (){
myInterceptor = this;
myInterceptor.usersDao = this.usersDao;
}
解決后調用方式:
Users users = myInterceptor.usersDao.findUsers(map);
解決該問題用到博客:
https://blog.csdn.net/georgeshaw1/article/details/74943089
2.
問題:在response.getWriter()時,報getWriter()has already been called for this response 異常
解決:
分析:該異常是response異常被其他調用了,我這里主要是因為在controller中有返回值給前端,然后在這里MyInterceptor中又用,就會導致這種情況
解決辦法:
使用 ServletOutputStream pw = response.getOutputStream();代替
3.
報有:
ava.io.CharConversionException: Not an ISO 8859-1 character: [失]
分析:這是因為使用getOutputStrieam不能輸出中文字符,改成英文就好了
4.多學習下注解的聲明和使用場景