分散権限 shiro + jwt + redis

1.分散許可技術の選択

springSecurity についてはJWT + Shiro + Redis を使用して、
多くの配布を行うことができます。ただ、この企画の最初に使ったshiroと、権限に応じた役割やメニューが書かれているので、本体はshiroを使っています。jwt を使用して、各ユーザーの ID を識別します。redis を使用して、各ユーザーのアクセス許可を保存します。次に、毎回 redis からアクセス許可を読み取ります。

2. jwt を理解する

JWT は、ヘッダー (ヘッダー)、ペイロード (ペイロード)、署名 (署名) の 3 つの部分で構成されます。送信中、JWT の 3 つの部分が Base64 でエンコードされ、. で連結されて、最終的な送信文字列が形成されます。
ペイロード セクションにユーザーの ID を入力します。ユーザーの ID は、jwt 文字列から解析できます。

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);
	}

トークンを生成し、ユーザー ID を保存し、アクセス許可を redis にロックします

4.shiroさんが作った設定

1.シロコンフィグ

 */
@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 構成はカスタム レルムを使用します

4.2 OAuth2レルム

/**
 * 认证
 *
 * @author Mark [email protected]
 */
@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失效,请重新登录");

        }
    }
}

このレルムには、認証と承認のロジックがあります。認証は最初にトークンを発行することです。jwt を使用して、トークンの信頼性を検証します。次に、権限情報を AuthenticationInfo に格納します。次に、認証方法でそれを削除します。
このユーザー クラスは、ストレージ用のアクセス許可フィールドも追加します。

	private Set<String> permissions;

4.4 残りは他の設定です

たとえば、redisの構成

/**
 * Redis配置
 *
 * @author Mark [email protected]
 */
@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

おすすめ

転載: blog.csdn.net/qq_21561833/article/details/127605241