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 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() {
                    @Override
                    public 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 grantedAuthorities = new ArrayList<>();
        //将前端传过来的username信息传入数据库比对,将匹配的用户信息存入user对象
        User user = userMapper.selectByName1(username) ;

        if (user == null){
            throw new UsernameNotFoundException("用户不存在");
        }


        //根据用户id进入user_role表中查询对应的角色id,存储到userRoles列表中
        List 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 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控制用户角色整个过程。

至此,结束。

相关