spring boot:在服务端用redis存储jwt登录后的用户信息(spring boot 2.4.4) 您所在的位置:网站首页 三年级上册数学期末考试试卷 spring boot:在服务端用redis存储jwt登录后的用户信息(spring boot 2.4.4)

spring boot:在服务端用redis存储jwt登录后的用户信息(spring boot 2.4.4)

2023-07-28 11:24| 来源: 网络整理| 查看: 265

一,用redis存储用户信息的好处?

1,避免解析token之后需要查库得到用户的信息

2,  因为jwt的token可以被反解,所以不直接使用username生成token,而是用一个随机的字符串代替

     避免安全问题

 

说明:刘宏缔的架构森林是一个专注架构的博客,

网站:https://blog.imgtouch.com本文: https://blog.imgtouch.com/index.php/2023/05/27/spring-boot-zai-fu-wu-duan-yong-redis-cun-chu-jwt-deng-lu/

         对应的源码可以访问这里获取: https://github.com/liuhongdi/

说明:作者:刘宏缔 邮箱: [email protected]

二,演示项目的相关信息

1,地址:

https://github.com/liuhongdi/jwtredis

2,功能说明:演示了用redis来存储jwt登录后的用户信息

3,项目结构:如图:

 

三,配置文件说明

1,pom.xml

org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security io.jsonwebtoken jjwt 0.9.1 org.springframework.boot spring-boot-starter-thymeleaf com.alibaba fastjson 1.2.73 org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2 com.fasterxml.jackson.core jackson-core 2.11.1 com.fasterxml.jackson.core jackson-databind 2.11.1 javax.xml.bind jaxb-api 2.3.0 com.sun.xml.bind jaxb-impl 2.3.0 com.sun.xml.bind jaxb-core 2.3.0 javax.activation activation 1.1.1 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.3 mysql mysql-connector-java runtime org.springframework.boot spring-boot-starter-test test

 

2,application.yml

spring: thymeleaf: cache: false encoding: UTF-8 mode: HTML prefix: classpath:/templates/ suffix: .html #mysql datasource: url: jdbc:mysql://localhost:3306/security?characterEncoding=utf8&useSSL=false username: root password: lhddemo driver-class-name: com.mysql.cj.jdbc.Driver #redis1 redis1: enabled: 1 host: 127.0.0.1 port: 6379 password: lhddemo database: 0 lettuce: pool: max-active: 32 max-wait: 300 max-idle: 16 min-idle: 8 #mybatis mybatis: mapper-locations: classpath:/mapper/*Mapper.xml type-aliases-package: com.example.demo.mapper

3,sql:

建表:

CREATE TABLE `sys_user` ( `userId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `userName` varchar(100) NOT NULL DEFAULT '' COMMENT '用户名', `password` varchar(100) NOT NULL DEFAULT '' COMMENT '密码', `nickName` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '昵称', PRIMARY KEY (`userId`), UNIQUE KEY `userName` (`userName`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表'

测试数据:

INSERT INTO `sys_user` (`userId`, `userName`, `password`, `nickName`) VALUES (1, 'lhd', '$2a$10$yGcOz3cdNI6Ya67tqQueS.raxzTOedGsv5jh2BwtRrI5/K9QEIPGq', '老刘'); 四,java代码说明

1,controller/HomeController.java

@Controller @RequestMapping("/home") public class HomeController { //session详情 @GetMapping("/session") @ResponseBody public RestResult session() { System.out.println("-------begin get session:"); if ( SessionUtil.getCurrentUser() == null) { return RestResult.error(ResponseCode.LOGIN_NEED); } else { Map data = new HashMap(); data.put("username", SessionUtil.getCurrentUser().getUsername()); data.put("userid", String.valueOf(SessionUtil.getCurrentUser().getUserid())); data.put("nickname", SessionUtil.getCurrentUser().getNickname()); data.put("roles", SessionUtil.getCurrentUser().getAuthorities().toString()); return RestResult.success(data); } } //显示getsession页面 @GetMapping("/getsession") public String get() { return "home/getsession"; } //显示login页面 @GetMapping("/login") public String login() { return "home/login"; } }

 

2,result/RestResult.java

public class RestResult implements Serializable { //uuid,用作唯一标识符,供序列化和反序列化时检测是否一致 private static final long serialVersionUID = 7498483649536881777L; //标识代码,0表示成功,非0表示出错 private Integer code; //提示信息,通常供报错时使用 private String msg; //正常返回时返回的数据 private Object data; public RestResult(Integer status, String msg, Object data) { this.code = status; this.msg = msg; this.data = data; } //返回成功数据 public static RestResult success(Object data) { return new RestResult(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getMsg(), data); } public static RestResult success(Integer code,String msg) { return new RestResult(code, msg, null); } //返回出错数据 public static RestResult error(ResponseCode code) { return new RestResult(code.getCode(), code.getMsg(), null); } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }

 

3,jwt/JwtAuthticationFilter.java

@Component public class JwtAuthticationFilter implements Filter { @Resource private AuthenticationManager authenticationManager; @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private JwtUserDetailsService userDetailsService; @Resource private UserRedisService userRedisService; @Resource private SysUserService sysUserService; @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("----------------AuthticationFilter init"); } //过滤功能 @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //得到当前的url HttpServletRequest request = (HttpServletRequest)servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String path = request.getServletPath(); if (path.equals("/auth/authenticate")) { System.out.println("auth path:"+path); //得到请求的post参数 String username = ""; String password = ""; try { BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream())); StringBuffer sb=new StringBuffer(); String s=null; while((s=br.readLine())!=null){ sb.append(s); } JSONObject jsonObject = JSONObject.parseObject(sb.toString()); username = jsonObject.getString("username"); password = jsonObject.getString("password"); //System.out.println("name:"+name+" age:"+age); } catch (IOException e) { e.printStackTrace(); } System.out.println("username:"+username); System.out.println("password:"+password); String authResult = ""; try{ authResult = authenticate(username,password); } catch (Exception e) { e.printStackTrace(); } System.out.println("authResult:"+authResult); if ("success".equals(authResult)) { SysUser oneUser = sysUserService.getOneUserByUsername(username);//数据库查询 看用户是否存在 final UserDetails userDetails = userDetailsService.loadUserBySysUser(oneUser); String origToken = jwtTokenUtil.makeTokenForSave(userDetails.getUsername()); final String token = jwtTokenUtil.generateTokenByOrig(origToken); //保存到redis oneUser.setOrigToken(origToken); oneUser.setPassword(""); //System.out.println("保存到redis的token:"+origToken); userRedisService.setOneUser(oneUser,origToken); //return result Map mapData = new HashMap(); mapData.put("token", token); ServletUtil.printRestResult(RestResult.success(mapData)); } else if ("badcredential".equals(authResult)){ ServletUtil.printRestResult(RestResult.error(ResponseCode.LOGIN_FAIL)); } else { ServletUtil.printRestResult(RestResult.error(ResponseCode.ERROR)); } return; } else { System.out.println("not auth path:"+path); filterChain.doFilter(servletRequest, servletResponse); } } @Override public void destroy() { System.out.println("----------------filter destroy"); } private String authenticate(String username, String password) throws Exception { try { System.out.println("username:"+username); System.out.println("password:"+password); authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); System.out.println("authenticate:will return success"); return "success"; } catch (DisabledException e) { throw new Exception("USER_DISABLED", e); } catch (BadCredentialsException e) { System.out.println("BadCredentialsException"); System.out.println(e.toString()); //throw new Exception("INVALID_CREDENTIALS", e); return "badcredential"; } } }

 

4,jwt/JwtRequestFilter.java

@Component public class JwtRequestFilter extends OncePerRequestFilter { @Autowired private JwtUserDetailsService jwtUserDetailsService; @Resource private UserRedisService userRedisService; @Autowired private JwtTokenUtil jwtTokenUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { final String requestTokenHeader = request.getHeader("Authorization"); String username = null; String jwtToken = null; // JWT Token 获取请求头部的 Bearer System.out.println("filter:header:"+requestTokenHeader); // only the Token if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { //System.out.println("filter :requestTokenHeader not null and start with bearer"); jwtToken = requestTokenHeader.substring(7); try { username = jwtTokenUtil.getUsernameFromToken(jwtToken); } catch (IllegalArgumentException e) { System.out.println("Unable to get JWT Token"); } catch (ExpiredJwtException e) { System.out.println("JWT Token has expired"); } catch (MalformedJwtException e) { System.out.println("JWT Token MalformedJwtException"); } } else { //System.out.println("filter :requestTokenHeader is null || not start with bearer"); //logger.warn("JWT Token does not begin with Bearer String"); } // 验证, if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { System.out.println("-----------get username from client:"+username); SysUser oneUser = userRedisService.getOneUserByUserToken(username); if (oneUser == null) { ServletUtil.printRestResult(RestResult.error(ResponseCode.LOGIN_NEED)); return; } //get UserDetails UserDetails userDetails = this.jwtUserDetailsService.loadUserBySysUser(oneUser); // JWT 验证通过 使用Spring Security 管理 if (jwtTokenUtil.validateTokenByOrigToken(jwtToken, oneUser.getOrigToken())) { UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } else { System.out.println("jwtTokenUtil.validateToken not success"); } } chain.doFilter(request, response); } }

 

5,jwt/JwtTokenUtil.java

@Component public class JwtTokenUtil implements Serializable { private static final long serialVersionUID = -2550185165626007488L; public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60; private String secret = "liuhongdi"; //retrieve username from jwt token public String getUsernameFromToken(String token) { return getClaimFromToken(token, Claims::getSubject); } //retrieve expiration date from jwt token public Date getExpirationDateFromToken(String token) { return getClaimFromToken(token, Claims::getExpiration); } public T getClaimFromToken(String token, Function claimsResolver) { final Claims claims = getAllClaimsFromToken(token); return claimsResolver.apply(claims); } //for retrieveing any information from token we will need the secret key private Claims getAllClaimsFromToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } //check if the token has expired private Boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date()); } //make a save token public String makeTokenForSave(String userName) { //得到当前时间 long time = System.nanoTime(); //得到随机数 Random ran = new Random(); int x = ran.nextInt(9000) + 1000; //md5后返回 String base = time+"_"+userName+"_"+x; String md5 = DigestUtils.md5DigestAsHex(base.getBytes()); return md5; } //generate token for user public String generateToken(UserDetails userDetails) { Map claims = new HashMap(); //String token = makeTokenForSave(userDetails.getUsername()); return doGenerateToken(claims, userDetails.getUsername()); } //generate token for user public String generateTokenByOrig(String origToken) { Map claims = new HashMap(); return doGenerateToken(claims, origToken); } //generate token private String doGenerateToken(Map claims, String subject) { return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)) .signWith(SignatureAlgorithm.HS512, secret).compact(); } //validate token public Boolean validateToken(String token, UserDetails userDetails) { final String username = getUsernameFromToken(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } //validate token by orig public Boolean validateTokenByOrigToken(String token, String origToken) { final String username = getUsernameFromToken(token); //System.out.println("username for valid:"+username); return (username.equals(origToken) && !isTokenExpired(token)); } }

 

6,jwt/JwtUserDetailsService.java

@Service public class JwtUserDetailsService implements UserDetailsService { @Resource private SysUserService sysUserService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { System.out.println("-----loadUserByUsername"); SysUser oneUser = sysUserService.getOneUserByUsername(username);//数据库查询 看用户是否存在 String encodedPassword = oneUser.getPassword(); Collection collection = new ArrayList();//权限集合 //用户角色role前面要添加ROLE_ List roles = oneUser.getRoles(); System.out.println(roles); for (String roleone : roles) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+roleone); collection.add(grantedAuthority); } //给用户增加用户id和昵称 SecUser user = new SecUser(username,encodedPassword,collection); user.setUserid(oneUser.getUserId()); user.setNickname(oneUser.getNickName()); return user; } public UserDetails loadUserBySysUser(SysUser oneUser) throws UsernameNotFoundException { System.out.println("-----loadUserByUser"); //SysUser oneUser = sysUserService.getOneUserByUsername(username);//数据库查询 看用户是否存在 String encodedPassword = oneUser.getPassword(); Collection collection = new ArrayList();//权限集合 //用户角色role前面要添加ROLE_ List roles = oneUser.getRoles(); System.out.println(roles); for (String roleone : roles) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+roleone); collection.add(grantedAuthority); } //给用户增加用户id和昵称 SecUser user = new SecUser(oneUser.getUserName(),encodedPassword,collection); user.setUserid(oneUser.getUserId()); user.setNickname(oneUser.getNickName()); return user; } }

 

7,impl/UserRedisServiceImpl.java

@Service public class UserRedisServiceImpl implements UserRedisService { @Resource private RedisTemplate redis1Template; //从redis查询查询得到用户信息 @Override public SysUser getOneUserByUserToken(String userToken){ System.out.println("从redis查询得到用户信息"); SysUser userOne; Object usersr = redis1Template.opsForValue().get("jwt_"+userToken); if (usersr == null) { userOne = null; } else { if (usersr.equals("-1")) { userOne = null; } else { userOne = (SysUser)usersr; } } return userOne; } //向redis写入用户信息,保存时长是jwt的配置 @Override public void setOneUser(SysUser user,String userToken){ long timeLenghth = JwtTokenUtil.JWT_TOKEN_VALIDITY; redis1Template.opsForValue().set("jwt_"+userToken,user,timeLenghth, TimeUnit.SECONDS); } }

 

8,impl/SysUserServiceImpl.java

@Service public class SysUserServiceImpl implements SysUserService { @Resource private UserMapper userMapper; //根据用户名查询数据库得到用户信息 @Override public SysUser getOneUserByUsername(String username) { System.out.println("从数据库查询得到用户信息"); SysUser user_one = userMapper.selectOneUserByUserName(username); return user_one; } }

 

9,其他相关代码可访问github

 

五,测试效果

1,访问login

http://127.0.0.1:8080/home/login

登录:

 

 2,访问session:

http://127.0.0.1:8080/home/getsession

点击 get session info按钮:

 

 3,从redis进行查询:

[root@localhost liuhongdi]# /usr/local/soft/redis/bin/redis-cli 127.0.0.1:6379> get jwt_73179e0988afc51896f7810df9716e52 "{\"@class\":\"com.jwtredis.demo.pojo.SysUser\",\"userId\":1,\"userName\":\"lhd\",\"password\":\"\",\"roles\":[\"java.util.ArrayList\",[]],\"nickName\":\"\xe8\x80\x81\xe5\x88\x98\",\"origToken\":\"73179e0988afc51896f7810df9716e52\"}"

 

六,查看spring boot的版本 . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.4.4)

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有