The API interface with a single entry uses shiro if permission authentication is performed.

problem description

in data communication, all data goes to the URL address of http://host/api/, and the business request code is used to control the result returned by the request. There may be a variety of situations in each request code. Different user roles control their permissions to use the interface, including different situations in an interface. In the use of Shiro, Do the bosses have a good solution


if different businesses have been designed to be controlled by request parameters, it is recommended to use the custom permission control of shiro to parse the corresponding business code and carry out permission control. This method is more flexible, not only can write dead permission rules, but also can cooperate with the database to make dynamic rule control.

all requests are intercepted, and the corresponding shiro configuration: / * * = perms;
inherits the org.apache.shiro.authz.Permission, rewrite implies method for permission verification.


Custom filter, override

isAccessAllowed

Custom realm, rewriting

doGetAuthorizationInfo
< H2 > I need to authorize according to the link parameters, and this is how I do it. < / H2 >
public class CustomRealm extends AuthorizingRealm

here is the authorization code

    /**
     * 
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.info("------------doGetAuthorizationInfo");
        User user = (User) principalCollection.getPrimaryPrincipal();

        List<String> userRoles = new ArrayList<String>();
        Set<String> categoryIds = new HashSet<>();
        QueryWrapper queryUserWrapper = new QueryWrapper();
        queryUserWrapper.eq("uuid",user.getUuid());
        user = userService.getOne(queryUserWrapper);
        if(null != user){

            Ugroup ugroup = ugroupService.getById(user.getGroupId());

            userRoles.add(ugroup.getName());

            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.eq("idx_group_id",ugroup.getId());
            List<GroupCategory> groupCategories = groupCategoryService.list(queryWrapper);
            if (groupCategories.size() > 0){
                for (GroupCategory groupCategory : groupCategories){
                    categoryIds.add(groupCategory.getCategoryId().toString());
                }
            }

        }else{
            throw new AuthorizationException();
        }
        //
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRoles(userRoles);
        authorizationInfo.addStringPermissions(categoryIds);
        return authorizationInfo;
    }
< H2 > override isAccessAllowed in filter < / H2 >
public class JWTFilter extends AuthorizationFilter {
 ...
 @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        logger.info("-----isAccessAllowed----");
        if (isLoginAttempt(request, response)) {
            try {
                executeLogin(request, response);
            } catch (Exception e) {
                response401(response);
            }
        }

        String id =  request.getParameter("id");
        Subject subject = getSubject(request,response);

        boolean flag = subject.isPermitted(id);
        return  flag;
    }
    ...
}
< H2 > requirements: authorize access to different types of articles for users belonging to different user groups < / H2 > < H2 > implementation idea: < / H2 >

the user information is obtained according to token in doGetAuthorizationInfo, the article types that can be accessed from the database are jointly queried, id, is added to the user rights, the parameters in the user access link are obtained in isAccessAllowed, and

is called.
subject.isPermitted(id);

determine whether you have this permission or not.

< H2 > Note: the subject.login (token); method must be called before subject.isPermitted (id) is called, otherwise authorization will not be carried out during isPermitted < / H2 > < H2 > paste all the codes below < / H2 > < H2 > CustomRealm.java < / H2 >
package com.zyc.shiro;


import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zyc.constant.CommonConstant;
import com.zyc.dao.GroupCategoryMapper;
import com.zyc.dao.UgroupMapper;
import com.zyc.entity.GroupCategory;
import com.zyc.entity.Ugroup;
import com.zyc.entity.User;
import com.zyc.redis.JwtRedisDAO;
import com.zyc.service.GroupCategoryService;
import com.zyc.service.UgroupService;
import com.zyc.service.UserService;
import com.zyc.util.JWTUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;

/**
 * 
 */
public class CustomRealm extends AuthorizingRealm {

    static Logger logger = LoggerFactory.getLogger(CustomRealm.class);

    @Autowired
    private UgroupService ugroupService;

    @Autowired
    private GroupCategoryService groupCategoryService;

    @Autowired
    private UserService userService;

    @Autowired
    private JwtRedisDAO jwtRedisDAO;



    /**
     * Shiro
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    /**
     * 
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.info("------------doGetAuthorizationInfo");
        User user = (User) principalCollection.getPrimaryPrincipal();

        List<String> userRoles = new ArrayList<String>();
        Set<String> categoryIds = new HashSet<>();
        QueryWrapper queryUserWrapper = new QueryWrapper();
        queryUserWrapper.eq("uuid",user.getUuid());
        user = userService.getOne(queryUserWrapper);
        if(null != user){

            Ugroup ugroup = ugroupService.getById(user.getGroupId());

            userRoles.add(ugroup.getName());

            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.eq("idx_group_id",ugroup.getId());
            List<GroupCategory> groupCategories = groupCategoryService.list(queryWrapper);
            if (groupCategories.size() > 0){
                for (GroupCategory groupCategory : groupCategories){
                    categoryIds.add(groupCategory.getCategoryId().toString());
                }
            }

        }else{
            throw new AuthorizationException();
        }
        //
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRoles(userRoles);
        authorizationInfo.addStringPermissions(categoryIds);
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
        logger.info("CustomRealm------------------doGetAuthenticationInfo");
        Map claims = JWTUtils.getClaims(CommonConstant.JWT_SECRET, (String) authenticationToken.getPrincipal());

        if (claims == null) {
            //
            throw new UnknownAccountException();
        }

        String uuid = (String) claims.get("uuid");
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("uuid",uuid);
        User user = userService.getOne(queryWrapper);
        //logger.info(user.toString());
        if(null == user){
            throw new UnknownAccountException();
        }

        String token = jwtRedisDAO.get(CommonConstant.ADMIN_JWT_PREFIX + uuid);
        //logger.info("token {}", authenticationToken.getPrincipal());
        if (token == null || !token.equals(authenticationToken.getPrincipal())) {
            throw new AuthorizationException();
        }

        //AuthenticatingRealmCredentialsMatcher
        return new SimpleAuthenticationInfo(
                //
                user,
                authenticationToken.getPrincipal(),
                //realm name
                getName()
        );

    }

}
< H2 > JWTFilter.java < / H2 >
package com.zyc.shiro;

import com.alibaba.fastjson.JSON;
import com.zyc.exception.enums.ErrorEnums;
import com.zyc.vo.RestResult;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class JWTFilter extends AuthorizationFilter {

    private static Logger logger = LoggerFactory.getLogger(JWTFilter.class);

    /**
     * 
     * headerAuthorization
     */
    //@Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        logger.info("-----isLoginAttempt----");
        HttpServletRequest req = (HttpServletRequest) request;
        String authorization = req.getHeader("Authorization");
        return authorization != null;
    }

    /**
     * 
     */
    //@Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        logger.info("-----executeLogin----");

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String authorization = httpServletRequest.getHeader("Authorization");

        JWTToken token = new JWTToken(authorization);
        // realm
        getSubject(request, response).login(token);
        // true
        return true;
    }

    /**
     * 
     *
     * true
     *  GET /article
     * 
     * false
     * trueController subject.isAuthenticated() 
     *  @RequiresAuthentication 
     * GET,POST()
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        logger.info("-----isAccessAllowed----");
        if (isLoginAttempt(request, response)) {
            try {
                executeLogin(request, response);
            } catch (Exception e) {
                response401(response);
            }
        }

        String id =  request.getParameter("id");
        Subject subject = getSubject(request,response);

        boolean flag = subject.isPermitted(id);
        return  flag;
    }

    /**
     * 
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        logger.info("-----preHandle----");

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", httpServletRequest.getMethod());
        httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));

        // optionoption
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return true;
        }
        return super.preHandle(request, response);
    }

    /**
     * onAccessDenied:; true ; false 
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) {
        logger.info(" isAccessAllowed  false  method onAccessDenied ");

        try {
            HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
            RestResult result = new RestResult();
            result.setCode(ErrorEnums.PERMISSION_DENIED.getCode());
            result.setMsg(ErrorEnums.PERMISSION_DENIED.getMsg());
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            PrintWriter out = httpServletResponse.getWriter();
            out.print(JSON.toJSONString(result));
            out.flush();
            out.close();
        } catch (IOException e) {
            logger.error(e.getMessage());
        }

        //  false 
        return false;
    }

    /**
     * code401
     */
    private void response401(ServletResponse resp) {
        logger.info("-----response401----");

        try {
            HttpServletResponse httpServletResponse = (HttpServletResponse) resp;

            RestResult result = new RestResult();
            result.setCode(ErrorEnums.TOKEN_MISS.getCode());
            result.setMsg(ErrorEnums.TOKEN_MISS.getMsg());
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            PrintWriter out = httpServletResponse.getWriter();
            out.print(JSON.toJSONString(result));

            out.flush();
            out.close();
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
    }
}
< H2 > JWTToken.java < / H2 >
package com.zyc.shiro;

import org.apache.shiro.authc.AuthenticationToken;

public class JWTToken implements AuthenticationToken {

    private String token;

    public JWTToken(String token) {
        this.token = token;
    }

    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return getPrincipal();
    }
}
< H2 > ShiroConfigurer.java < / H2 >
package com.zyc;

import com.zyc.shiro.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.apache.shiro.authz.permission.RolePermissionResolver;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.*;

@Configuration
public class ShiroConfigurer {

    private static final Logger logger = LoggerFactory.getLogger(ShiroConfigurer.class);

    /**
     * ShiroWebFactory :shiroFilter<br />
     *
     * @param securityManager
     * @return
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        logger.info("ShiroWeb-->shiroFilter {}", ShiroFilterFactoryBean.class);
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // Shiro,
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // (URL),,Web"/login.jsp"
        // shiroFilterFactoryBean.setLoginUrl("/login")
        // ,
        // shiroFilterFactoryBean.setSuccessUrl("/index")
        // ,
        // shiroFilterFactoryBean.setUnauthorizedUrl("/pages/403")
        /* shiro,FormAuthenticationFilterFormAuthenticationFilter
         **
         */

        /*shiro  Map
         * Mapkey(xmlvalue)'/'HttpServletRequest.getContextPath()
         * anon:,,.do.jsp*,login.jsp?main
         * authc:,Shiroorg.apache.shiro.web.filter.authc.FormAuthenticationFilter
         */
        // jwt
        Map filterMap = new HashMap();
        filterMap.put("jwt", new JWTFilter());
        shiroFilterFactoryBean.setFilters(filterMap);


        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // ,Shiro

        // <!--  /** -->:
        // <!-- authc:url; anon:url-->
        // filterChainDefinitionMap.put("/webui/**", "anon")
        // filterChainDefinitionMap.put("/webjars/**", "anon")
        //filterChainDefinitionMap.put("/sys/login", "anon");
        //filterChainDefinitionMap.put("/sys/logout", "anon");
        //filterChainDefinitionMap.put("/token/callback", "anon");
        //filterChainDefinitionMap.put("/dist", "anon");
        //filterChainDefinitionMap.put("/download/excel", "anon");
        //api
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        // JWT Filter
        filterChainDefinitionMap.put("/**", "jwt");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    @Bean
    public JWTFilter jwtFilter() {
        return new JWTFilter();
    }

    /**
     * Shiro Realm AuthorizingRealmRealm,Shiro
     *
     * @param
     * @return managerRealm
     */
    @Bean
    public CustomRealm userRealm() {
        CustomRealm userRealm = new CustomRealm();
        // realm,credentialsMatcher
        // userRealm.setCredentialsMatcher(hashedCredentialsMatcher())
        userRealm.setCachingEnabled(false);
        //
        return userRealm;
    }

    /**
     * bean
     *
     * @return
     * @Bean(name = "securityManager")
     */
    @Bean
    public SecurityManager securityManager() {
        logger.info("ShiroWeb-->securityManager {}", ShiroFilterFactoryBean.class);
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm());
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        // session
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        return securityManager;
    }


    /**
     * 
     * ShiroSimpleAuthenticationInfo
     * doGetAuthenticationInfo;
     * 
     *  
     *
     * @return
     */
    @Bean(name = "credentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("SHA-512");
        // md5(md5(""))
        hashedCredentialsMatcher.setHashIterations(2);
        //storedCredentialsHexEncodedtrueHex;falseBase64
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }

    /**
     * Shiro
     *
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * Shiro(@RequiresRoles,@RequiresPermissions),SpringAOPShiro,
     * bean(DefaultAdvisorAutoProxyCreator()AuthorizationAttributeSourceAdvisor)
     *
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        advisorAutoProxyCreator.setUsePrefix(true);
        return advisorAutoProxyCreator;
    }

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

}
< H2 > pom.xml < / H2 >
<!--aop-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--jwt-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.7.0</version>
</dependency>
<!-- Shiro-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>
Menu