分布式权限 shiro + jwt + redis(第三期)

这个分布式权限应用于我的开源项目中
目前已经写的文章有
目前已经写的文章有。并且有对应视频版本。

b站上面本期视频版本,观看视频食用更佳!点击即可跳转,找不到视频可以直接搜索我 目前叫 呆呆呆呆梦

git项目地址 【IM即时通信系统(企聊聊)】点击可跳转
sprinboot单体项目升级成springcloud项目 【第一期】
前端项目技术选型以及页面展示【第二期】
分布式权限 shiro + jwt + redis【第三期】
给为服务添加运维模块 统一管理【第四期】
微服务数据库模块【第五期】
netty与mq在项目中的使用(第六期)】
分布式websocket即时通信(IM)系统构建指南【第七期】

1.分布式权限技术选型

使用的是JWT +Shiro + Redis
关于springSecurity 做分布式可能挺多。但是自己这个项目最开始使用的shiro,而且权限对应的角色,菜单都已经写好了,所以主体采用的shiro。使用jwt 标识每个用户的身份。使用redis 存储每个用户的权限。然后每次都从redis里面读取权限。

2.理解jwt

JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串。
在payload部分放上存储用户的id。然后可以从jwt字符串中解析出用户的id。

3.目前的登录逻辑

/**
	 * 登录
	 */
	@PostMapping("/sys/login")
	public GenericResponse login(@RequestBody SysLoginForm form)throws IOException {
		//用户信息
		SysUserEntity user = sysUserService.queryByUserName(form.getUsername());
		CommonUser commonUser = new CommonUser();
		commonUser.setOpenid(user.getOpenid());
		commonUser.setUserId(user.getUserId());
		//账号不存在、密码错误
		if(user == null || !user.getPassword().equals(new Sha256Hash(form.getPassword(), user.getSalt()).toHex())) {
			return GenericResponse.response(ServiceError.LOGIN_ERROR_USERNAME);

		}
		String token = "";
		try {
			token = JwtTokenUtil.generateToken(commonUser);
		} catch (Exception e) {
			e.printStackTrace();
		}

		Set<String> permsSet = shiroService.getUserPermissions(user.getUserId());
		Gson gson = new Gson();
		redisTemplate.opsForValue().set(user.getUserId().toString(), gson.toJson(permsSet));
		return GenericResponse.response(ServiceError.NORMAL, token);
	}

生成token,将用户id和锁拥有的权限存储进入redis

4.shiro所做的配置

1.ShiroConfig
 */
@Configuration
public class ShiroConfig {

    @Bean("securityManager")
    public SecurityManager securityManager(OAuth2Realm oAuth2Realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(oAuth2Realm);
        securityManager.setRememberMeManager(null);
        return securityManager;
    }



    //============================到目前为止是新加入的东西,具体还需要看效果

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);

        //oauth过滤
        Map<String, Filter> filters = new HashMap<>();
        filters.put("oauth2", new OAuth2Filter());
        shiroFilter.setFilters(filters);

        Map<String, String> filterMap = new LinkedHashMap<>();
        //这里放置自己通行的静态资源
//        filterMap.put("/", "anon");
        filterMap.put("/downloadFile","anon");
        filterMap.put("/parseByName","anon");

        //静态资源不拦截
        filterMap.put("/202108*/**", "anon");
        filterMap.put("/config/**", "anon");
//        是不是必须加上/代表是static 目录下面的
        filterMap.put("/index.html", "anon");
        //================================//
        filterMap.put("/webjars/**", "anon");
        filterMap.put("/druid/**", "anon");
        filterMap.put("/app/**", "anon");
        filterMap.put("/shrio/login", "anon");
        filterMap.put("/sys/login", "anon");
        filterMap.put("/sys/registByWeb", "anon");
        filterMap.put("/swagger/**", "anon");
        filterMap.put("/v2/api-docs", "anon");
        filterMap.put("/swagger-ui.html", "anon");
        filterMap.put("/swagger-resources/**", "anon");
        filterMap.put("/captcha.jpg", "anon");
        filterMap.put("/aaa.txt", "anon");
//        filterMap.put("/**", "anon");
        filterMap.put("/**", "oauth2");
        shiroFilter.setFilterChainDefinitionMap(filterMap);

        return shiroFilter;
    }

    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

}

securityManager配置里面使用自定的realm

4.2 OAuth2Realm
/**
 * 认证
 *
 * @author Mark 1942951600@qq.com
 */
@Component
public class OAuth2Realm extends AuthorizingRealm {
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof OAuth2Token;
    }
    /**
     * 授权(验证权限时调用)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SysUserEntity user = (SysUserEntity)principals.getPrimaryPrincipal();
        Set<String> permsSet = user.getPermissions();
        //用户权限列表
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(permsSet);
        return info;
    }



    /**
     * 认证(登录时调用)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String accessToken = (String) token.getPrincipal();
        Long userId;
        try {
            Claims claims = JwtTokenUtil.parseJWT(accessToken);
            userId =  Long.valueOf(claims.get("userid").toString());
            Gson gson = new Gson();
            String  permsSetString = ( String )redisTemplate.opsForValue().get(userId.toString());
            Set<String> permsSet=  gson.fromJson(permsSetString,Set.class);
            SysUserEntity user = new SysUserEntity();
            user.setUserId(userId);
            user.setPermissions(permsSet);
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, getName());
            return info;
        } catch (Exception e) {
            e.printStackTrace();

            throw new IncorrectCredentialsException("token失效,请重新登录");

        }
    }
}

在这个realm里面有认证和授权的逻辑。认证是先去出token。利用jwt校验token真实性。然后往AuthenticationInfo存入权限信息。然后再授权方法中取出。
这个用户类也添加了Permission字段用来存储。

	private Set<String> permissions;

4.4 剩下的就是一些其他配置

比如redis 的配置

/**
 * Redis配置
 *
 * @author Mark 1942951600@qq.com
 */
@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory factory;

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    @Bean
    public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
        return redisTemplate.opsForValue();
    }

    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }

    @Bean
    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForSet();
    }

    @Bean
    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}

定义shiro的过滤器

public class OAuth2Filter extends AuthenticatingFilter {

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        //获取请求token
        String token = getRequestToken((HttpServletRequest) request);

        if(StringUtils.isBlank(token)){
            return null;
        }

        return new OAuth2Token(token);
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){
            return true;
        }

        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        //获取请求token,如果token不存在,直接返回401
        String token = getRequestToken((HttpServletRequest) request);
        if(StringUtils.isBlank(token)){
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());

            String json = new Gson().toJson(R.error(HttpStatus.SC_UNAUTHORIZED, "invalid token"));

            httpResponse.getWriter().print(json);

            return false;
        }

        return executeLogin(request, response);
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=utf-8");
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
        try {
            //处理登录失败的异常
            Throwable throwable = e.getCause() == null ? e : e.getCause();
            R r = R.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage());

            String json = new Gson().toJson(r);
            httpResponse.getWriter().print(json);
        } catch (IOException e1) {

        }

        return false;
    }

    /**
     * 获取请求的token
     */
    private String getRequestToken(HttpServletRequest httpRequest){
        //从header中获取token
        String token = httpRequest.getHeader("token");

        //如果header中不存在token,则从参数中获取token
        if(StringUtils.isBlank(token)){
            token = httpRequest.getParameter("token");
        }

        return token;
    }


}

4.5 还有最重要的需要让别的模块可以引入。

在这里插入图片描述
在META-INFO里面这样配置。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.rose.permission.config.ShiroConfig,\
  com.rose.permission.oauth2.OAuth2Realm,\
  com.rose.permission.config.RedisConfig

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
【资源说明】 1、基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 2、该资源包括项目的全部源码,下载可以直接使用! 3、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 4、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip 基于springboot+shiro+jwt+vue+redis的后台管理系统源码.zip
课程简介:历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值