Springboot integrated permission management framework apache shiro

1. Explanation of terms

There are a lot of them online

2. pom dependency

<dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.2.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
        <version>1.2.2</version>
    </dependency>

encache is optional and is mainly used for caching during authentication.

3. shiroConfiguration

The configuration of shiro is mainly the settings of shiroFilter and securityManager

@Component
public class ShiroConfiguration {
<br>
    @Bean
    public EhCacheManager ehCacheManager()
    {
        EhCacheManager manager = new EhCacheManager();
        manager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
        return manager;
    }

    @Resource
    private MyShiroRealm myShiroRealm;
    @Bean
    public SecurityManager securityManager()
    {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(myShiroRealm);
        defaultWebSecurityManager.setCacheManager(ehCacheManager());
        return defaultWebSecurityManager;
    }
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
    {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String,String> map = new LinkedHashMap<>();
        Map<String,Filter> filterMap = new LinkedHashMap<>();
        filterMap.put("authc",loginFilter());
        filterMap.put("perms",myFilter());
        shiroFilterFactoryBean.setFilters(filterMap);

        //map.put("/RPCAFA2A208FA648EA27C1EC30CADFC8B3D","anon");
        //map.put("/**","authc");
        map.put("/RPC52CA3404FDAADAB18F91E8210DFCE1522","perms[admin:test]");
        map.put("/RPC66EED9EBACF5FB42B9AD9C069495587F","perms[test]");
        map.put("/**","authc");
        //map.put("/**","myfilter");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager)
    {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /*@Bean
    public MyShiroRealm myShiroRealm()
    {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        //myShiroRealm.setCacheManager(manager);
        return myShiroRealm;
    }*/
    @Bean
    public LoginFilter loginFilter()
    {
        return new LoginFilter();
    }
    @Bean
    public MyFilter myFilter()
    {
        return new MyFilter();
    }
}

ehCahceManager is the registration cache manager, MyShiroRealm is the specific implementation of permission authentication and needs to be registered in the securityManager. ShiroFilter mainly sets the filter chain. I mainly use two filters here, perms and authc.

Perms is used to set access permissions for accessing URLs, such as map.put(“/RPC52CA3404FDAADAB18F91E8210DFCE1522”, “perms[admin:test]”), key is the accessed URL, and value is the set permissions, which must be in the form of perms[]. I put the URL of the interface that needs to be accessed into the database, and configure the permissions for each interface that requires authentication by reading the database during the initial configuration. authc requires login authentication.

Since we separate the front-end and the back-end, the front-end accesses the back-end service through the RPC interface. In this case, I want to return different codes to the front-end when there is no permission or login, so I implemented two filters myself. The loginFilter replaces the original authc filter. filter, myFilter replaces the original perms filter.

4. Implementation of MyFilter and LoginFilter

loginFilter inherits AuthenticationFilter, myFilter inherits PermissionsAuthorizationFilter

public class LoginFilter extends AuthenticationFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        Subject subject = getSubject(servletRequest,servletResponse);
        String path = request.getServletPath();
        System.out.println("path = " + path);
        if(path.equals("/RPCAFA2A208FA648EA27C1EC30CADFC8B3D"))
        {
            return true;
        }
        if(subject.getPrincipals() != null)
        {
            return true;
        }
        return false;
    }
    /**
     * If the session times out or permission verification fails, 401 will be returned and prompted by a pop-up window on the front-end page.
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
            throws IOException {

        ShiroUtil.writeResponse((HttpServletResponse) response,new Result(AuthorizationStatus.NOT_LOGIN));<br>Return false;<br><br>}<br>}

First, the isAccessAllowed method will be executed. I will exclude several login interfaces here and return true directly, which means that the login interface does not require login authentication. When false is returned, the onAccessDenied method is executed. Here I directly return the front-end json data through the response stream.

public class ShiroUtil {

    /**
     * Determine whether certification is required
     * @param bean
     * @return true not required false required
     */
    public static boolean isContains(NotAuthorizationBean bean)
    {
        List<String> paths = bean.getPaths();
        String name = bean.getName();
        for(String path : paths)
        {
            if(path.equalsIgnoreCase(name))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * Unified return of front-end json data
     * @param response
     * @param data
     */
    public static void writeResponse(HttpServletResponse response, Object data)
    {
        try {
            response.setContentType("application/json");
            OutputStream outputStream = response.getOutputStream();
            outputStream.write(JSON.toJSONString(data).getBytes("UTF-8"));
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public enum AuthorizationStatus {
    NOT_LOGIN(401,"Not logged in"),
    NOT_AUTHORIZATION(403,"No authorization")
    ;

    private Integer code;

    private String msg;

    AuthorizationStatus(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}
public class Result {

    private Integer c;

    private String d;

    public Result(AuthorizationStatus status)
    {
        this.c = status.getCode();
        this.d = status.getMsg();
    }

    public Result(Integer c, String d) {
        this.c = c;
        this.d = d;
    }

    public Integer getC() {
        return c;
    }

    public void setC(Integer c) {
        this.c = c;
    }

    public String getD() {
        return d;
    }

    public void setD(String d) {
        this.d = d;
    }
}

The implementation of myFilter is similar, and it also overrides the isAccessAllowed and onAccessDenied methods.

public class MyFilter extends PermissionsAuthorizationFilter {
    @Override
    public boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws IOException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        String path = request.getServletPath();
        System.out.println("request path = " + path);
        Subject subject = getSubject(servletRequest,servletResponse);
        if(path.equals("/RPCAFA2A208FA648EA27C1EC30CADFC8B3D"))
        {
            return true;
        }
       /* String[] perms = (String[])((String[])o);
        boolean isPermitted = true;
        if(perms != null & amp; & amp; perms.length > 0) {
            if(perms.length == 1) {
                if(!subject.isPermitted(perms[0])) {
                    isPermitted = false;
                }
            } else if(!subject.isPermittedAll(perms)) {
                isPermitted = false;
            }
        }*/

        return super.isAccessAllowed(servletRequest,servletResponse,o);
    }
    /**
     * If the session times out or permission verification fails, 401 will be returned and prompted by a pop-up window on the front-end page.
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
            throws IOException {
        System.out.println("no permission");
        Subject subject = getSubject(request,response);
        if(subject.getPrincipal() == null)
        {
            ShiroUtil.writeResponse((HttpServletResponse) response,new Result(AuthorizationStatus.NOT_LOGIN));
        }else{
            ShiroUtil.writeResponse((HttpServletResponse) response,new Result(AuthorizationStatus.NOT_AUTHORIZATION));
        }
        return false;
    }
}

5. MyShiroRealm implementation

Realm is a specific implementation of permissions and login management. It needs to inherit AuthorizingRealm to implement doGetAuthorizationInfo permission authentication and doGetAuthenticationInfo login authentication.

@Service
public class MyShiroRealm extends AuthorizingRealm {
    @Resource
    private UserInfoService userInfoService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("Authorization authentication doGetAuthorizationInfo()");
        String username = (String) super.getAvailablePrincipal(principalCollection);
        System.out.println("username = " + username);
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addStringPermission("admin:test");
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("Login authentication doGetAuthenticationInfo()");
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        System.out.println("token = " + token.getUsername());
        UserInfo userInfo = userInfoService.findByUsername(token.getUsername());
        if(userInfo != null)
        {
            return new SimpleAuthenticationInfo(userInfo.getUsername(),userInfo.getPassword(),getName());
        }
        return null;
    }
}