来点直接的:
github上已经发布了相关的源码:https://github.com/78654Majesty/user 谢谢你的关注和点赞 谢谢!
什么是jwt?
JWT官网: https://jwt.io/
JWT(Java版)的github地址:https://github.com/jwtk/jjwt
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON
的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于通信双方之间以JSON
对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC
算法或者是RSA
的公私秘钥对进行签名。
image.png
1. 用户使用账号和面发出post请求;
2. 服务器使用私钥创建一个jwt;
3. 服务器返回这个jwt给浏览器;
4. 浏览器将该jwt串在请求头中像服务器发送请求;
5. 服务器验证该jwt;
6. 返回响应的资源给浏览器。
身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。 信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。
1.简洁(Compact): 可以通过URL
,POST
参数或者在HTTP header
发送,因为数据量小,传输速度也很快
2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
3.因为Token
是以JSON
加密的形式保存在客户端的,所以JWT
是跨语言的,原则上任何web形式都支持。
4.不需要在服务端保存会话信息,特别适用于分布式微服务。
`
JWT是由三段信息构成的,将这三段信息文本用.
连接一起就构成了JWT字符串。
就像这样:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
JWT包含了三部分:
Header 头部(标题包含了令牌的元数据,并且包含签名和/或加密算法的类型)
Payload 负载 (类似于飞机上承载的物品)
Signature 签名/签证
Header
JWT的头部承载两部分信息:token类型和采用的加密算法。
{
"alg": "HS256",
"typ": "JWT"
}
声明类型:这里是jwt
声明加密的算法:通常直接使用 HMAC SHA256
加密算法是单向函数散列算法,常见的有MD5、SHA、HAMC。
MD5(message-digest algorithm 5) (信息-摘要算法)缩写,广泛用于加密和解密技术,常用于文件校验。校验?不管文件多大,经过MD5后都能生成唯一的MD5值
SHA (Secure Hash Algorithm,安全散列算法),数字签名等密码学应用中重要的工具,安全性高于MD5
HMAC (Hash Message Authentication Code),散列消息鉴别码,基于密钥的Hash算法的认证协议。用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。常用于接口签名验证
Payload
载荷就是存放有效信息的地方。
有效信息包含三个部分
1.标准中注册的声明
2.公共的声明
3.私有的声明
标准中注册的声明 (建议但不强制使用) :
iss
: jwt签发者sub
: 面向的用户(jwt所面向的用户)aud
: 接收jwt的一方exp
: 过期时间戳(jwt的过期时间,这个过期时间必须要大于签发时间)nbf
: 定义在什么时间之前,该jwt都是不可用的.iat
: jwt的签发时间jti
: jwt的唯一身份标识,主要用来作为一次性token
,从而回避重放攻击。
公共的声明 :
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有的声明 :
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64
是对称解密的,意味着该部分信息可以归类为明文信息。
Signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
header (base64后的)
payload (base64后的)
secret
这个部分需要base64
加密后的header
和base64
加密后的payload
使用.
连接组成的字符串,然后通过header
中声明的加密方式进行加盐secret
组合加密,然后就构成了jwt
的第三部分。
密钥secret
是保存在服务端的,服务端会根据这个密钥进行生成token
和进行验证,所以需要保护好。
项目结构
在maven仓库https://mvnrepository.com/ 可以查询自己需要的jar或者需要转化的jar
创建springboot web项目 加入redis、tk通用mapper 、谷歌java工具包guava、以及jjwt
plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'java'
id 'war'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.security'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
// mavenCentral()
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-web'
// implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.0.0'
implementation 'com.alibaba:druid-spring-boot-starter:1.1.10'
implementation group: 'redis.clients', name: 'jedis', version: '3.0.1'
//java工具包
implementation group: 'com.google.guava', name: 'guava', version: '27.0.1-jre'
// https://mvnrepository.com/artifact/tk.mybatis/mapper-spring-boot-starter
implementation group: 'tk.mybatis', name: 'mapper-spring-boot-starter', version: '2.1.5'
implementation group: 'tk.mybatis', name: 'mapper', version: '4.1.5'
implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.0'
runtimeOnly 'mysql:mysql-connector-java'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
需要登录才能进行操作的注解@Login
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {
boolean required() default true;
}
@Target
:注解的作用目标
@Target(ElementType.TYPE)
——接口、类、枚举、注解@Target(ElementType.FIELD)
——字段、枚举的常量@Target(ElementType.METHOD)
——方法@Target(ElementType.PARAMETER)
——方法参数@Target(ElementType.CONSTRUCTOR)
——构造函数@Target(ElementType.LOCAL_VARIABLE)
——局部变量@Target(ElementType.ANNOTATION_TYPE)
——注解@Target(ElementType.PACKAGE)
——包
@Retention
:注解的保留位置
RetentionPolicy.SOURCE
:这种类型的Annotations
只在源代码级别保留,编译时就会被忽略,在class
字节码文件中不包含。RetentionPolicy.CLASS
:这种类型的Annotations
编译时被保留,默认的保留策略,在class
文件中存在,但JVM
将会忽略,运行时无法获得。RetentionPolicy.RUNTIME
:这种类型的Annotations
将被JVM
保留,所以他们能在运行时被JVM
或其他使用反射机制的代码所读取和使用。@Document
:说明该注解将被包含在javadoc
中@Inherited
:说明子类可以继承父类中的该注解
本人对jwt生成token进行了封装 JwtUtil
package com.security.demo.util;
import com.security.demo.entity.CurrentUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.security.PrivateKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* Jtw工具类
*
* @author fanglingxiao
*/
public class JwtUtil {
private static Logger logger = LoggerFactory.getLogger(JwtUtil.class);
private static final String PAYLOAD_UID = "uid";
private static final String PAYLOAD_USERNAME = "userName";
private static final String PAYLOAD_REALNAME = "realName";
private static final String PAYLOAD_EXPIREIN = "expireIn";
private static final String PAYLOAD_DEVICE_ID = "deviceId";
public JwtUtil() {
}
/**
* 加载Properties
*
* @return Properties
*/
private static Properties getProperties() {
Properties properties = new Properties();
try {
properties.load(JwtUtil.class.getResourceAsStream("/application.properties"));
} catch (IOException e) {
logger.error("getProperties IOException", e);
}
return properties;
}
/**
* 创建jwt
*
* @param id (JWT ID):是JWT的唯一标识
* @param subject WT的主体
* @param ttlMillIn 过期的时间戳
* @return String String
*/
public static String createJWT(String id, CurrentUser currentUserVo, String subject, long ttlMillIn) {
//指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256;
//生成JWT的时间
Date iat = new Date();
//创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
Map claims = new HashMap<>(16);
claims.put(PAYLOAD_UID, currentUserVo.getUid());
claims.put(PAYLOAD_USERNAME, currentUserVo.getUserName());
claims.put(PAYLOAD_REALNAME, currentUserVo.getRealName());
claims.put(PAYLOAD_DEVICE_ID,currentUserVo.getDeviceId());
claims.put(PAYLOAD_EXPIREIN, ttlMillIn);
//生成签名的时候使用的秘钥secret,这个方法本地封装了的,一般可以从本地配置文件中读取。它就是你服务端的私钥。
PrivateKey key = RSACryptographyUtil.getPrivateKey(getProperties().getProperty("token.privateKey"));
//下面就是在为payload添加各种标准声明和私有声明了
JwtBuilder builder = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", signatureAlgorithm.getValue())
//如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setClaims(claims)
//设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
//.setId(id)
//iat: jwt的签发时间
.setIssuedAt(iat)
//sub(Subject):代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roleid之类的,作为用户的唯一标志。
//.setSubject(subject)
//设置签名使用的签名算法和签名使用的秘钥
.signWith(signatureAlgorithm, key);
if (ttlMillIn >= 0) {
//设置过期时间Expiration(默认24小时)
builder.setExpiration(new Date(iat.getTime() + 24 * 60 * 60 * 1000));
}
//压缩为xxxxxxxxxxxxxx.xxxxxxxxxxxxxxx.xxxxxxxxxxxxx这样的jwt
return builder.compact();
}
/**
* 解析jwt
*
* @param jsonWebToken jsonWebToken
* @return Claims
*/
public static Claims parseJwt(String jsonWebToken) {
if (jsonWebToken != null && jsonWebToken.startsWith("Bearer ")) {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(RSACryptographyUtil.getPublicKey(getProperties()
.getProperty("token.publicKey"))).parseClaimsJws(jsonWebToken.substring(7)).getBody();
return claims;
} catch (Exception e) {
logger.error("ParseJwt have a exception ",e);
throw new RuntimeException("TOKEN解析错误");
}
} else {
logger.error("JsonWebToken is not conform to the regulation");
throw new RuntimeException("TOKEN解析错误");
}
}
public static String getUid(String token) {
Claims claims = parseJwt(token);
return (String) claims.get(PAYLOAD_UID);
}
public static String getUserName(String token) {
Claims claims = parseJwt(token);
return (String) claims.get(PAYLOAD_USERNAME);
}
public static String getRealName(String token) {
Claims claims = parseJwt(token);
return (String) claims.get(PAYLOAD_REALNAME);
}
public static String getDeviceId(String token) {
Claims claims = parseJwt(token);
return (String) claims.get(PAYLOAD_DEVICE_ID);
}
public static long getExpireIn(String token) {
Claims claims = parseJwt(token);
return (long) claims.get(PAYLOAD_EXPIREIN);
}
/**
* 测试
* Bearer :票据
*/
public static void main(String[] args) {
String jwtToken = "Bearer " + createJWT("1", new CurrentUser(), "{\"uId\":\"123\"}", System.currentTimeMillis() + 1000);
logger.info("jwt={}", jwtToken);
String uId = getUid(jwtToken);
logger.info("uId={}", uId);
}
}
以及使用RSA的公私钥Util
package com.security.demo.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* RSA工具类
*
* @author fanglingxiao
*/
public class RSACryptographyUtil {
private static Logger logger = LoggerFactory.getLogger(RSACryptographyUtil.class);
/**
* 将base64编码后的公钥字符串转成PublicKey实例.
*
* @author fanglingxiao
* @date 2019-03-18
* @param publicKey publicKey
* @return PublicKey
*/
public static PublicKey getPublicKey(String publicKey) {
try {
byte[] keyBytes = Base64.getDecoder().decode(publicKey.getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
} catch (Exception e) {
logger.error("获取PublicKey实例失败:", e);
}
return null;
}
/**
* 将base64编码后的私钥字符串转成PrivateKey实例.
*
* @author fanglingxiao
* @date 2019-03-18
* @param privateKey privateKey
* @return PublicKey
*/
public static PrivateKey getPrivateKey(String privateKey) {
try {
byte[] keyBytes = Base64.getDecoder().decode(privateKey.getBytes());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
} catch (Exception e) {
logger.error("获取PrivateKey实例:", e);
}
return null;
}
}
工具类完成书写 那就需要从网关开始 拦截器、controller、service、dao。。。
跟着我一起学java
创建工程就不说了,用springboot脚手架 勾选web mybatis mysql redis
数据库就创建简单的user表
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(255) COLLATE utf8mb4_bin NOT NULL,
`real_name` varchar(255) COLLATE utf8mb4_bin NOT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_name` (`user_name`) USING BTREE COMMENT '用户名唯一索引'
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
生成好了 书写properties文件
server.port=8082
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/fang?serverTimezone=GMT%2B8
spring.datasource.druid.username=root
spring.datasource.druid.password=root
spring.application.name=fang-gtw
token.publicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM4xQ0/nzoyyc4I21OyHYWkJXp3zqjKT5CBNc8IL8j1o1ER6UqdL+b52mQ/wZ30yOOka5Z6rKealHVBZIncRosUCAwEAAQ==
token.privateKey=MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAzjFDT+fOjLJzgjbU7IdhaQlenfOqMpPkIE1zwgvyPWjURHpSp0v5vnaZD/BnfTI46Rrlnqsp5qUdUFkidxGixQIDAQABAkB7LhL4eZN7mmBHOlqZs4cOKPrNgfP7oz51zc+J7XkaFaIt0MYp69OVushedliv0K4ulUFwJP2WAnZ7x1O9TSFBAiEA/2CP7ovIYCycJOQapXKBOaYcHhceaKmiAd8h+ATCBPcCIQDOsf5Y1+r6ilTegdbcrdWFmIzZspxAzNyGr+VbljBzIwIgGC/OEfq9NJalBz1vZKIOqSYZXaQ05hbC2EiI0BOrSIsCIQCUkPei4D+zqa5q6KSJpiEtj4LeDzWklhuEa/XNqNxWRQIhAPD5yAvCL+fzPz0YVCRAJE2idOxVpFb5mxkraVlKgLnQ
#配置.xml文件路径
mybatis.mapper-locations=classpath*:mapper/*.xml
#配置模型路径
mybatis.type-aliases-package=com.security.demo.entity
mybatis.configuration.map-underscore-to-camel-case=true
# Redis数据库索引(默认为0)
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=1s
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
#spring.redis.timeout=0
在创建的时候有个问题需要提醒一下:
第一个就是使用tk的通用mapper 需要把springboot-mybatis-start jar修改成tk的启动jar包
第二个就是mapperScan的包也是一样 使用tk的包
个人觉得这样挺麻烦的 使用逆向生成对应的service dao model mapper 还是很舒服的,具体如何操作 我其他的博客也有 ,自行百度也是ok的 很简单的配置一下就行了 。或者使用mybatis-plus更是完美!!!
接下来重点来了 就是过滤器的书写
package com.security.demo.auth;
import com.alibaba.druid.support.json.JSONUtils;
import com.google.common.collect.Maps;
import com.security.demo.util.JwtUtil;
import com.security.demo.util.ResultApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
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.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
/**
* 登陆拦截器
* ctrl + o 接口重写快捷键
* @author fanglingxiao
* @date 2019/3/26
*/
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取请求头的参数
String token = request.getHeader("Authorization");
if (!(handler instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
//当前的方法是否包含Login注解
if (!method.isAnnotationPresent(Login.class)){
return true;
}
Login login = method.getAnnotation(Login.class);
if (!login.required()){
return false;
}
//验证token
if (!checkToken(token)){
//token有误
ResultApi res = new ResultApi();
res.setResCode(-1);
res.setResMsg("请重新登录");
response.setContentType("application/json;charset=utf-8");
response.getWriter().append(JSONUtils.toJSONString(res));
return false;
}
String userName = JwtUtil.getUserName(token);
String realName = JwtUtil.getRealName(token);
Map currentUser = Maps.newHashMap();
currentUser.put("userName",userName);
currentUser.put("realName",realName);
request.setAttribute("currentUser",currentUser);
return true;
}
private boolean checkToken(String token){
// 执行认证
if (null == token) {
throw new RuntimeException("无token,请重新登录");
}
if (!token.startsWith("Bearer ")) {
throw new RuntimeException("token格式有误,请重新登录");
}
String userName = JwtUtil.getUserName(token);
if (StringUtils.isEmpty(userName)){
return false;
}
//获取redis中的token信息
String tokenInRedis = redisTemplate.opsForValue().get(userName);
if (StringUtils.isEmpty(tokenInRedis)){
//如果redis挂了可以保证正常执行
return true;
}
String realName = JwtUtil.getRealName(token);
long expireIn = JwtUtil.getExpireIn(token);
if (StringUtils.isEmpty(realName)){
return false;
}
String userNameInRedis = JwtUtil.getUserName(tokenInRedis);
String realNameInRedis = JwtUtil.getRealName(tokenInRedis);
long expireInRedis = JwtUtil.getExpireIn(tokenInRedis);
if (StringUtils.isEmpty(userNameInRedis) || StringUtils.isEmpty(realNameInRedis)){
return false;
}
//判断token是否过期
if (expireIn != expireInRedis){
return false;
}
//判断token过期时间
return expireIn >= System.currentTimeMillis();
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
过滤器写好之后 该如何让他运行呢?那就需要写个配置类
package com.security.demo.config;
import com.security.demo.auth.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author fanglingxiao
* @date 2019/3/26
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor()).addPathPatterns("/**");
}
@Bean
public LoginInterceptor loginInterceptor(){
return new LoginInterceptor();
}
}
这样基础操作就完成了,接下来就是网关服务层的书写
package com.security.demo.controller;
import com.security.demo.auth.Login;
import com.security.demo.entity.CurrentUser;
import com.security.demo.entity.User;
import com.security.demo.service.impl.UserServiceImpl;
import com.security.demo.util.ResultApi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
/**
* @author fanglingxiao
* @date 2019/3/26
*/
@RestController
@RequestMapping("**/user")
public class UserController {
@Autowired
private UserServiceImpl userService;
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@PostMapping("register")
public ResultApi register(@Valid @RequestBody CurrentUser model){
ResultApi res = new ResultApi<>();
if (StringUtils.isEmpty(model.getPassword()) || StringUtils.isEmpty(model.getUserName())
|| StringUtils.isEmpty(model.getRealName())){
res.setResCode(-1);
res.setResMsg("注册有误!请输入有效参数");
}
userService.register(model);
res.setResCode(200);
res.setResMsg("注册成功!");
return res;
}
@PostMapping("login")
public ResultApi login(@Valid @RequestBody CurrentUser model){
logger.info("login params {}",model);
String token = userService.login(model);
ResultApi res = new ResultApi<>();
if (StringUtils.isEmpty(token)){
res.setResCode(-1);
res.setResMsg("用户不存在!请重新登陆");
return res;
}
res.setResCode(200);
res.setResMsg("登陆成功!");
res.setDate(token);
return res;
}
@PostMapping("queryUserList")
@Login
public ResultApi> queryUserList(HttpServletRequest request){
List users = userService.queryUserList();
ResultApi> res = new ResultApi<>();
res.setDate(users);
Map currentUser = (Map) request.getAttribute("currentUser");
String userName = (String) currentUser.get("userName");
return res;
}
}
对应的service
package com.security.demo.service.impl;
import com.security.demo.dao.UserDao;
import com.security.demo.entity.CurrentUser;
import com.security.demo.entity.User;
import com.security.demo.service.UserService;
import com.security.demo.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author fanglingxiao
* @date 2019/3/26
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private RedisTemplate redisTemplate;
public String login(CurrentUser model) {
String userName = model.getUserName();
String password = model.getPassword();
User user = userDao.selectUserByName(userName);
if (null == user){
return "";
}
if (!StringUtils.isEmpty(password) && password.equals(user.getPassword())){
//create token
CurrentUser c = new CurrentUser();
c.setUserName(user.getUserName());
c.setRealName(user.getRealName());
String jwtToken = JwtUtil.createJWT("1", c, "{\"uId\":\"123\"}", System.currentTimeMillis() + 1000*60*60*24);
redisTemplate.opsForValue().set(user.getUserName(),"Bearer "+jwtToken,1, TimeUnit.DAYS);
return jwtToken;
}
return "";
}
public void register(CurrentUser model) {
String password = model.getPassword();
String realName = model.getRealName();
String userName = model.getUserName();
User user = new User();
user.setPassword(password);
user.setUserName(userName);
user.setRealName(realName);
userDao.insert(user);
}
public List queryUserList() {
return userDao.selectAll();
}
}
对应的dao
package com.security.demo.dao;
import com.security.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import tk.mybatis.mapper.common.BaseMapper;
/**
* @author fanglingxiao
* @createTime 2019/3/26
*/
@Mapper
public interface UserDao extends BaseMapper {
/**
* 使用用户名查询用户信息
* @param userName username
* @return User
*/
User selectUserByName(String userName);
}
对应的user.xml
这样就操作完成了
继续加油把 长期关注我哦
相关的代码已经在github上 地址:https://github.com/78654Majesty/user