SpringBoot+Mybatis+PostMan(十):用户角色权限访问控制三(禁用session、启用token并集成redis)
Springboot+Mybatis+redis+postman项目实战总目录*
第二篇章:用户角色权限访问控制
SpringBoot+Mybatis+PostMan(十):用户角色权限访问控制三(禁用session、启用token并集成redis)
在上一篇文章中,对于用户角色权限访问控制实现,我们利用了spring boot Security自带的session的cookie串接起访问整个过程,进而实现角色访问控制 , 这次,我们进行进一步的探讨,舍弃springBoot Security自带session,转而自定义生成token,将token存储于redis,进而实现通过redis中的token串联起整个过程,这是我们现在的整体思路,接下来就对此部分的实现进行讲解。准备工作上一篇文章已经写好了,现在我们直接进入正题。
项目代码下载地址:https://github.com/yeyuting-1314/tesetdemo_roleControll-2.0-tokenAndRedis.git
一、正题
1. 在上一篇文章的基础上,我们要添加一个JWTAuthorizationFilter过滤类,用于判断用户是否是第一次登陆(通过token进行判定)
/** * @author yeyuting * @create 2021/1/29 */ //验证成功后开始鉴权 public class JWTAuthorizationFilter extends BasicAuthenticationFilter { @Autowired JedisUtil jedisUtil ; public JWTAuthorizationFilter(AuthenticationManager authenticationManager) { super(authenticationManager); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String tokenHeader = request.getHeader(JwtTokenUtils.TOKEN_HEADER); //String roleHeader = request.getHeader(JwtTokenUtils.ROLE_HEADER) ; // 如果请求头中没有Authorization信息则直接放行了 if (tokenHeader == null) { super.doFilterInternal(request, response, chain); return; } // 如果请求头中有token,则进行解析,并且设置认证信息 ??? SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader)); super.doFilterInternal(request, response, chain); } // 从token中获取用户信息 private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) { String token = tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, ""); //去redis里面拿token 确认redis中存在和token对应的值 Jedis jedis = new Jedis("localhost" , 6379) ; String username = jedis.get(token) ; List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(jedis.get(JwtTokenUtils.ROLE_HEADER)); grantedAuthorities.add(grantedAuthority) ; if (username != null){ return new UsernamePasswordAuthenticationToken(username, null, grantedAuthorities); } return null; } }
2. 接着实现JwtTokenUtils类,用于存储前端头信息。
/** * @author yeyuting * @create 2021/1/29 */ public class JwtTokenUtils { public static final String TOKEN_HEADER = "token"; public static final String ROLE_HEADER = "authority"; public static final String TOKEN_PREFIX = "Bearer"; }
3. webConfig类禁用session
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { @Override public <O extends FilterSecurityInterceptor> O postProcess(O object) { object.setSecurityMetadataSource(cfisms()); object.setAccessDecisionManager(cadm()); return object ; } }) .antMatchers("/userLogin").permitAll() // 所有访问该应用的http请求都要通过身份认证才可以访问 .anyRequest().authenticated() .and().httpBasic() .and() .csrf().disable() // 指定登陆URL .formLogin() .loginProcessingUrl("/userLogin") .successHandler(customAuthenticationSuccessHandler) .failureHandler(customAuthenticationFailureHandler) .and() .exceptionHandling().accessDeniedHandler(authenticationAccessDeniedHandler) .authenticationEntryPoint(simpleAuthenticationEntryPoint) .and() .addFilter(new JWTAuthorizationFilter(authenticationManager())) // 不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); }
4. UserServiceImpl类loadUserByUsername方法实现token生成和存入redis:
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //角色和权限共用GrantedAuthority接口,后面采集到的角色信息将存储到grantedAuthorities集合中 List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); //将前端传过来的username信息传入数据库比对,将匹配的用户信息存入user对象 User user = userMapper.selectByName1(username) ; if (user == null){ throw new UsernameNotFoundException("用户不存在"); } //根据用户id进入user_role表中查询对应的角色id,存储到userRoles列表中 List<UserRole> userRoles = userRoleMapper.selectList(user.getId()) ; //遍历userRoles集合 for (UserRole ur : userRoles){ //再根据用户id查到对应的role,此时就拿到了用户对应的角色 Role role = roleMapper.selectOne1(ur.getRoleId()); //将对应的角色存储到权限grantedAuthorities集合中 GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getRole()); grantedAuthorities.add(grantedAuthority); } //生成token //将原有的token值全部干掉,防止重复登陆 Jedis jedis = jedisUtil.getResource(); //存入键值对 String jedisKey = jedis.get(user.getUserName()) ; if(jedisKey != null){ jedisUtil.delString(user.getUserName()); } String token = tokenUtil.generateToken(user) ; user.setToken(token); user.setGrantedAuthority("authority"); user.setGrantedAuthorities(grantedAuthorities); jedisUtil.tokenToJedis(user); jedis.close(); //测试数据 String name = user.getUserName() ; //创建一个用户,用于判断权限,请注意此用户名和方法参数中的username一致;BCryptPasswordEncoder是用来演示加密使用。 // //这里主要是实现用户名和密码的核对,如果信息都正确才给开这个权限,这是一种安全策略 return new org.springframework.security.core.userdetails.User ("user", new BCryptPasswordEncoder().encode(user.getPassword()), grantedAuthorities); }
5. 在User类中添加两个属性
String grantedAuthority ; List<GrantedAuthority> grantedAuthorities ; //getter and setter
6. JedisUtil类的tokenToJedis方法进行更新修改,如下:
public void tokenToJedis(User user){ Jedis jedis = getResource() ; jedis.set(user.getUserName() , user.getToken()) ; jedis.expire(user.getUserName() , Constants.TOKEN_EXPIRE_TIME) ; //存储对象 jedis.set(user.getGrantedAuthority(), user.getGrantedAuthorities().toString()) ; // jedis.set(user.getGrantedAuthorities().toString() , user.getGrantedAuthority()) ; for(GrantedAuthority grantedAuthority : user.getGrantedAuthorities()){ jedis.set(user.getGrantedAuthority() , grantedAuthority.toString()) ; System.out.println("redis中getGrantedAuthorities值为:" + jedis.get(user.getGrantedAuthority())); } jedis.set(user.getToken() , user.getUserName()) ; jedis.expire(user.getToken() , Constants.TOKEN_EXPIRE_TIME) ; Long currentTime =System.currentTimeMillis() ; jedis.set(user.getUserName()+user.getToken() , currentTime.toString()) ; //用完关闭 jedis.close(); System.out.println("redis中token值为:" + jedis.get(user.getUserName())); System.out.println("redis中用户信息值为:" + jedis.get(user.getToken())); // System.out.println("redis中getGrantedAuthorities值为:" + jedis.get(user.getGrantedAuthority())); }
这样一来,代码逻辑就实现了,接下来前端模拟实现。
二、前端实现
1. 用户登陆认证 实现token生成和当前登陆所需用户角色。
redis记录token和对应的角色信息。
2. 接着来访问selectStartIndexAndPageSize接口,第一次登陆时候逻辑上会提示用户进行登陆。
紧接着将token值给到接口头信息中,继续测试,发现这时候顺利访问到数据库,并将方法顺利实现,这是因为我们之前在数据库中将此接口交给了普通用户角色,所以这样一来,自然就能实现访问,这就实现了整个过程 。
这便是整个token结合redis控制用户角色整个过程。
至此,结束。