当前位置: 首页 > news >正文

城乡建设杂志社官方网站ps在线网页版

城乡建设杂志社官方网站,ps在线网页版,域名申请好后 如何建设网站,如何用手机做音乐网站最近在使用SpringSecurityJWT实现认证授权的时候#xff0c;出现Redis在反序列化userDetails的异常。通过实践发现#xff0c;使用不同的序列化方法和不同的fastJson版本#xff0c;异常信息各不相同。所以特地记录了下来。 一、项目代码 先来看看我项目中redis相关配置信息…        最近在使用SpringSecurityJWT实现认证授权的时候出现Redis在反序列化userDetails的异常。通过实践发现使用不同的序列化方法和不同的fastJson版本异常信息各不相同。所以特地记录了下来。 一、项目代码 先来看看我项目中redis相关配置信息。 1.自定义的redis序列化器 import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; import com.alibaba.fastjson.parser.ParserConfig; import org.springframework.util.Assert; import java.nio.charset.Charset;/*** Redis使用FastJson序列化** author mosul*/ public class FastJsonRedisSerializerT implements RedisSerializerT {public static final Charset DEFAULT_CHARSET Charset.forName(UTF-8);private ClassT clazz;static{ParserConfig.getGlobalInstance().setAutoTypeSupport(true);}public FastJsonRedisSerializer(ClassT clazz){super();this.clazz clazz;}Overridepublic byte[] serialize(T t) throws SerializationException{if (t null){return new byte[0];}return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);}Overridepublic T deserialize(byte[] bytes) throws SerializationException{if (bytes null || bytes.length 0){return null;}String str new String(bytes, DEFAULT_CHARSET);return JSON.parseObject(str, clazz);}protected JavaType getJavaType(Class? clazz){return TypeFactory.defaultInstance().constructType(clazz);} } 2.redis配置类 import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer;Configuration public class RedisConfig {/*** 指定特定的连接工厂* return*//*Beanpublic RedisConnectionFactory redisConnectionFactory() {return new LettuceConnectionFactory();}*/BeanSuppressWarnings(value { unchecked, rawtypes })public RedisTemplateObject, Object redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplateObject, Object template new RedisTemplate();template.setConnectionFactory(connectionFactory);FastJsonRedisSerializer serializer new FastJsonRedisSerializer(Object.class);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;} } 3.redis工具类 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.BoundSetOperations; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Component;import java.util.*; import java.util.concurrent.TimeUnit;/*** Redis帮助类** author mosul*/ SuppressWarnings(value { unchecked, rawtypes }) Component public class RedisHelper {Autowiredpublic RedisTemplate redisTemplate;/*** 缓存基本的对象Integer、String、实体类等** param key 缓存的键值* param value 缓存的值*/public T void setCacheObject(final String key, final T value){redisTemplate.opsForValue().set(key, value);}/*** 缓存基本的对象Integer、String、实体类等** param key 缓存的键值* param value 缓存的值* param timeout 时间* param timeUnit 时间颗粒度*/public T void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit){redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}/*** 设置有效时间** param key Redis键* param timeout 超时时间* return true设置成功false设置失败*/public boolean expire(final String key, final long timeout){return expire(key, timeout, TimeUnit.SECONDS);}/*** 设置有效时间** param key Redis键* param timeout 超时时间* param unit 时间单位* return true设置成功false设置失败*/public boolean expire(final String key, final long timeout, final TimeUnit unit){return redisTemplate.expire(key, timeout, unit);}/*** 获得缓存的基本对象。** param key 缓存键值* return 缓存键值对应的数据*/public T T getCacheObject(final String key){ValueOperationsString, T operation redisTemplate.opsForValue();return operation.get(key);}/*** 删除单个对象** param key*/public boolean deleteObject(final String key){return redisTemplate.delete(key);}/*** 删除集合对象** param collection 多个对象* return*/public long deleteObject(final Collection collection){return redisTemplate.delete(collection);}/*** 缓存List数据** param key 缓存的键值* param dataList 待缓存的List数据* return 缓存的对象*/public T long setCacheList(final String key, final ListT dataList){Long count redisTemplate.opsForList().rightPushAll(key, dataList);return count null ? 0 : count;}/*** 获得缓存的list对象** param key 缓存的键值* return 缓存键值对应的数据*/public T ListT getCacheList(final String key){return redisTemplate.opsForList().range(key, 0, -1);}/*** 缓存Set** param key 缓存键值* param dataSet 缓存的数据* return 缓存数据的对象*/public T BoundSetOperationsString, T setCacheSet(final String key, final SetT dataSet){BoundSetOperationsString, T setOperation redisTemplate.boundSetOps(key);IteratorT it dataSet.iterator();while (it.hasNext()){setOperation.add(it.next());}return setOperation;}/*** 获得缓存的set** param key* return*/public T SetT getCacheSet(final String key){return redisTemplate.opsForSet().members(key);}/*** 缓存Map** param key* param dataMap*/public T void setCacheMap(final String key, final MapString, T dataMap){if (dataMap ! null) {redisTemplate.opsForHash().putAll(key, dataMap);}}/*** 获得缓存的Map** param key* return*/public T MapString, T getCacheMap(final String key){return redisTemplate.opsForHash().entries(key);}/*** 往Hash中存入数据** param key Redis键* param hKey Hash键* param value 值*/public T void setCacheMapValue(final String key, final String hKey, final T value){redisTemplate.opsForHash().put(key, hKey, value);}/*** 获取Hash中的数据** param key Redis键* param hKey Hash键* return Hash中的对象*/public T T getCacheMapValue(final String key, final String hKey){HashOperationsString, String, T opsForHash redisTemplate.opsForHash();return opsForHash.get(key, hKey);}/*** 删除Hash中的数据** param key* param hkey*/public void delCacheMapValue(final String key, final String hkey){HashOperations hashOperations redisTemplate.opsForHash();hashOperations.delete(key, hkey);}/*** 获取多个Hash中的数据** param key Redis键* param hKeys Hash键集合* return Hash对象集合*/public T ListT getMultiCacheMapValue(final String key, final CollectionObject hKeys){return redisTemplate.opsForHash().multiGet(key, hKeys);}/*** 获得缓存的基本对象列表** param pattern 字符串前缀* return 对象列表*/public CollectionString keys(final String pattern){return redisTemplate.keys(pattern);} } 4.自己系统中的UserDetails Data NoArgsConstructor AllArgsConstructor public class LoginUser implements UserDetails {private static final long serialVersionUID 1L;// 系统用户private SysUser user;// 用户权限列表private ListSysPermission permissionList;Overridepublic Collection? extends GrantedAuthority getAuthorities() {return permissionList.stream().filter(permission - permission.getPermission() ! null).map(permission - new SimpleGrantedAuthority(permission.getPermission())).collect(Collectors.toList());}Overridepublic String getPassword() {return user.getPassword();}Overridepublic String getUsername() {return user.getUsername();}Overridepublic boolean isAccountNonExpired() {return true;}Overridepublic boolean isAccountNonLocked() {return true;}Overridepublic boolean isCredentialsNonExpired() {return true;}Overridepublic boolean isEnabled() {return true;} } 5.登录设置 Overridepublic String login(SysUser sysUser) {String token null;//密码需要客户端加密后传递try {UserDetails userDetails sysUserService.loadUserByUsername(sysUser.getUsername());if(!passwordEncoder.matches(sysUser.getPassword(),userDetails.getPassword())){throw new BadCredentialsException(密码不正确);}UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authentication);token jwtTokenUtil.generateToken(userDetails);String key login: sysUser.getUsername();//设置redisredisHelper.setCacheObject(key,userDetails);//insertLoginLog(username);} catch (AuthenticationException e) {LOGGER.warn(登录异常:{}, e.getMessage());}return token;}Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {SysUser sysUser sysUserMapper.selectOne(new QueryWrapperSysUser().eq(username, username));ListSysPermission permissionsByUser sysUserRoleMapper.findPermissionsByUser(sysUser.getUserId());sysUser.setPassword(new BCryptPasswordEncoder().encode(sysUser.getPassword()));// 将系统的用户信息和权限信息封装成UserDetailsUserDetails userDetail new LoginUser(sysUser, permissionsByUser);return userDetail;} 6.JWT校验 Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {String authHeader request.getHeader(this.tokenHeader);if (authHeader ! null authHeader.startsWith(this.tokenHead)) {String authToken authHeader.substring(this.tokenHead.length());// The part after Bearer String username jwtTokenUtil.getUserNameFromToken(authToken.trim());LOGGER.info(checking username:{}, username);if (username ! null SecurityContextHolder.getContext().getAuthentication() null) {//从redis中获取userDetailsString redisKey login: username;UserDetails userDetails redisHelper.getCacheObject(redisKey);if(Objects.isNull(userDetails)){throw new RuntimeException(用户未登录);}if (jwtTokenUtil.validateToken(authToken, userDetails)) {//存入SecurityContextHolder//TODO 获取权限信息封装到Authentication中UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));LOGGER.info(authenticated user:{}, username);SecurityContextHolder.getContext().setAuthentication(authentication);}}}//放行filterChain.doFilter(request, response);} 7.fastjson版本 !--fastjson依赖--!--第一个版本--dependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.47/version/dependency!--第二个版本--dependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion2.0.22/version/dependency 上面的代码中先根据用户名获取用户对应的用户信息和权限信息然后构建SpringSecurity的UserDetails对象用户登录的时候将这个UserDetails对象放入redis中后续校验请求携带的token与redis中的信息是否一致。 二、异常信息 1.版本一报错信息 需要说明的是在redis系列化时是正常的对应的值也成功设置近缓存了但是在JWT校验阶段执行UserDetails userDetails redisHelper.getCacheObject(redisKey);时出现异常反序列化失败。 针对这个问题首先上面的代码逻辑是没有问题的但是与fastjson反序列化不兼容导致的问题。 根据异常信息提示设置属性authorities错误猜想下是因为LoginUser中没有authorities属性但也说不过去同样没有属性username和password怎么不会报错 带着这个疑问我们先给将LoginUser代码改为下面这种形式。 Data public class LoginUser implements UserDetails {private static final long serialVersionUID 1L;private SysUser user;private ListSysPermission permissionList;private ListGrantedAuthority authorities;public LoginUser() {}public LoginUser(SysUser user, ListSysPermission permissionList) {this(user,permissionList,null);}/*** 针对fastJson中redis反序列化报错的改进* org.springframework.data.redis.serializer.SerializationException:* Could not deserialize: set authorities error; nested exception is com.alibaba.fastjson.JSONException: set authorities error** param user* param permissionList* param authorities*/public LoginUser(SysUser user, ListSysPermission permissionList, ListGrantedAuthority authorities) {//返回当前用户的权限ListGrantedAuthority authoritieList permissionList.stream().filter(permission - permission.getPermission() ! null).map(permission - new SimpleGrantedAuthority(permission.getPermission())).collect(Collectors.toList());this.user user;this.permissionList permissionList;this.authorities authoritieList;}Overridepublic Collection? extends GrantedAuthority getAuthorities() {return this.authorities;}Overridepublic String getPassword() {return user.getPassword();}Overridepublic String getUsername() {return user.getUsername();}Overridepublic boolean isAccountNonExpired() {return true;}Overridepublic boolean isAccountNonLocked() {return true;}Overridepublic boolean isCredentialsNonExpired() {return true;}Overridepublic boolean isEnabled() {return true;} } 发现这里改完之后是可以正常运行的。 而且发现当我们给getAuthorities()赋简单的值的时候不会出现这个问题。 Overridepublic Collection? extends GrantedAuthority getAuthorities() {ListGrantedAuthority authoritieList new ArrayList();authoritieList.add(new SimpleGrantedAuthority(ROLE_ADMIN));return authoritieList;} 而且我们在构造函数中提前将权限列表加载出来赋值给一个属性属性不一定非得名为authorities如属性名为authoritiesList。然后返回这个属性也不会报错。 Overridepublic Collection? extends GrantedAuthority getAuthorities() {return this.authoritieList;} 通过上面两个测试可以猜测下如果构造函数中提前将比较复杂的实现暴露了反系列化也不会报错。可能出现异常的原因fastjson是对不确定结果无法反系列化如果是简单的结果或在构造器中就已经知道了确定结果就不会出现反序列化的异常。  2.版本二报错信息 在使用fastJson 2.x版本的时候同时需要对redis配置类做如下修改。 BeanSuppressWarnings(value { unchecked, rawtypes })public RedisTemplateObject, Object redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplateObject, Object template new RedisTemplate();template.setConnectionFactory(connectionFactory);/*java.lang.ClassCastException:* com.alibaba.fastjson.JSONObject cannot be cast to org.springframework.security.core.userdetails.UserDetails* */String[] acceptNames {org.springframework.security.core.authority.SimpleGrantedAuthority};GenericFastJsonRedisSerializer serializer new GenericFastJsonRedisSerializer(acceptNames);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;} 修改完成之后还是会出现设置属性authorities错误同样需要对LoginUser做上述修改。 3.发现FastJson反系列的一般问题 正如上面所说的同样没有属性username和password怎么不会报错于是做了一系列测试。发现了在低版本的fastJson中对应集合类型接口方法中包含较复杂的实现不是直接显示赋值反序化可能要求必须有对应的属性。 定义了一个有不同返回值类型的几种方法来测试。 public interface CrazyDetails {ListString getApps();User getUser();String getName();String[] getNodes();CollectionString getTests();ListUser getUsers(); } 定义一个实现类 Data NoArgsConstructor AllArgsConstructor public class MosulApp implements CrazyDetails{Overridepublic User getUser() {return new User(this.appInfo.name);}private AppInfo appInfo;Overridepublic ListUser getUsers() {ListUser userList new ArrayList();for(int i 0; i this.appInfo.name.length(); i ) {User user1 new User( i);userList.add(user1);}return userList;}Overridepublic String[] getNodes() {String[] strings new String[2];strings new String[]{this.appInfo.name,this.appInfo.details};return strings;}/*private ListString apps;*///报错添加需要private ListString testsOverridepublic CollectionString getTests() {ListString list Arrays.asList(appInfo.name);return list;}Overridepublic ListString getApps() {ListString objects new ArrayList();for(int i 0; i this.appInfo.name.length(); i ) {objects.add(tt i);}return objects;}Overridepublic String getName() {return this.appInfo.name;}} 在测试发现fastJson 1.x版本对于Arrays.asList(appInfo.name);反序列化失败fastJson 2.x版本则可以反序列化成功但对于UserDetails中Collection? extends GrantedAuthority getAuthorities()中如果有比较复杂的实现fastJson 2.x版本反序列化还是会失败。所以为了保险起见最后在自定义的UserDetails中添加authorities属性除了这种方法能外应该也跟自定义的序列化器相关设置有关需要进行探索。 三、Redis和SpringSecutiry相关配置 基于上述测试最终fastJson选用2.0.22版本最后将redis配置类和SpringSecutiry中UserDetails实现类修改为如下所示。 1.redis配置类 Configuration public class RedisConfig {/*** 指定特定的连接工厂* return*//*Beanpublic RedisConnectionFactory redisConnectionFactory() {return new LettuceConnectionFactory();}*/BeanSuppressWarnings(value { unchecked, rawtypes })public RedisTemplateObject, Object redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplateObject, Object template new RedisTemplate();template.setConnectionFactory(connectionFactory);/* FastJsonRedisSerializer serializer new FastJsonRedisSerializer(Object.class);*//*解决java.lang.ClassCastException:* com.alibaba.fastjson.JSONObject cannot be cast to org.springframework.security.core.userdetails.UserDetails* */String[] acceptNames {org.springframework.security.core.authority.SimpleGrantedAuthority};GenericFastJsonRedisSerializer serializer new GenericFastJsonRedisSerializer(acceptNames);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;} } 2.LoginUser类 Data public class LoginUser implements UserDetails {private static final long serialVersionUID 1L;// 用户信息private SysUser user;// 用户权限列表private ListSysPermission permissionList;// SpringSecurity对应的权限信息private ListGrantedAuthority authorities;public LoginUser() {}public LoginUser(SysUser user, ListSysPermission permissionList) {this(user,permissionList,null);}/*** 针对fastJson中redis反序列化报错的改进* org.springframework.data.redis.serializer.SerializationException:* Could not deserialize: set authorities error; nested exception is com.alibaba.fastjson.JSONException: set authorities error** param user* param permissionList* param authorities*/public LoginUser(SysUser user, ListSysPermission permissionList, ListGrantedAuthority authorities) {//返回当前用户的权限ListGrantedAuthority authoritieList permissionList.stream().filter(permission - permission.getPermission() ! null).map(permission - new SimpleGrantedAuthority(permission.getPermission())).collect(Collectors.toList());this.user user;this.permissionList permissionList;this.authorities authoritieList;}Overridepublic Collection? extends GrantedAuthority getAuthorities() {return this.authorities;}Overridepublic String getPassword() {return user.getPassword();}Overridepublic String getUsername() {return user.getUsername();}Overridepublic boolean isAccountNonExpired() {return true;}Overridepublic boolean isAccountNonLocked() {return true;}Overridepublic boolean isCredentialsNonExpired() {return true;}Overridepublic boolean isEnabled() {return true;} }
http://www.dnsts.com.cn/news/30403.html

相关文章:

  • 北京谁会做网站开发江苏网站seo平台
  • 网站建设需要哪些信息外网搭建
  • 什么是网站开发相亲交友小程序源码
  • 代练中介网站有得做吗建立的意思解释
  • 交互式网站是什么wordpress 欢迎插件
  • 网站没有关键词库上海卖房网站
  • 北京金方网站设计二手网站建设目标
  • 济南网站制作建设厦门人才网招聘最新信息
  • php 网站开发平台商业网站开发需求
  • 利用表单大师做网站网站怎么设计好看的图片
  • 网站头部样式企业网络规划设计
  • 美工网站模板推荐坪地网站建设
  • 连城县建设局网站个人公众号做电影网站吗
  • 江苏齐力建设集团网站用html5做手机网站
  • 旅游网站建设怎么做wordpress文章自动发布功能
  • 如何在vps上建设网站江西网站设计欣赏
  • 什么是a站在局域网内访问本机的asp网站
  • 网站初期缺点网站开发与建设会计分录
  • 个人网站备案材料填写沛县建设工程交易网
  • 昆明做网站费用wordpress网站数据库备份
  • 电子商务网站分类网站建设佰首选金手指十六
  • 白酒网站设计石家庄在线制作网站
  • 做网站网站推广赚佣金网站开发实习总结
  • 中国建设银行安徽省 招聘信息网站软件开发培训班哪个好
  • 可以免费秒玩游戏的网站沈阳网站建设三好街
  • 贵阳网站页面设计沂源网站建设
  • 厦门市建设局网站规划标准服务平台型网站
  • 青岛网站建设代理加盟郑州有做彩票网站的吗
  • 简述从网站规划的角度常见的网站模式成都市新津县建设局官方网站
  • 安徽元鼎建设工程网站没人注意的暴利行业