• Shiro权限管理系统使用手册
  • 发布于 2个月前
  • 300 热度
    0 评论
前言

要说基于JAVA的权限管理系统有哪些,最让人推崇的当属Shiro 。Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。---摘自百度百科

今天我们就来讲解一下Shiro的具体用法。


表设计
至少需要以下几张表,具体如何设计要视自己的业务而定:
sys_user:用户表
sys_role:角色表
sys_menu:菜单表
sys_user_role:用户对应角色
sys_role_menu:用户对应菜单
整合
pom.xml引入:
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring-boot-web-starter</artifactId>
   <version>1.4.2</version>
</dependency>
定义 UserRealm:
/**
 * 堆代码 duidaima.com
 * 用户认证
 */
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private SysUserService userService;

    /**
     * 获取授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        Long userId = ShiroUtils.getUserId();
        List<String> rolesSet = userService.listUserRoles(userId);
        List<String> permsSet = userService.listUserPerms(userId);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setRoles(new HashSet<>(rolesSet));
        info.setStringPermissions(new HashSet<>(permsSet));
        return info;
    }

    /**
     * 获取认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        String username = (String) authenticationToken.getPrincipal();
        String password = new String((char[]) authenticationToken.getCredentials());
        SysUser user = userService.getUser(username);
        if (user == null) {
            throw new UnknownAccountException("账户不存在");
        }
        if(!password.equals(user.getPassword())) {
            throw new IncorrectCredentialsException("密码不正确");
        }
        return new SimpleAuthenticationInfo(user, password, getName());
    }
}
定义 ShiroConfig:
/**
 * Shiro权限配置
 */
@Configuration
public class ShiroConfig {

    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean (SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置SecuritManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //设置登录页
        shiroFilterFactoryBean.setLoginUrl("/login.shtml");
        // 登录成功后要跳转的链接,如果是ajax这里无效
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        // 拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边
        // authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
        /**
         * 静态文件
         */
        filterChainDefinitionMap.put("/css/**","anon");
        filterChainDefinitionMap.put("/images/**","anon");
        filterChainDefinitionMap.put("/js/**","anon");
        filterChainDefinitionMap.put("/file/**","anon");
        /**
         * 登录注册
         */
        filterChainDefinitionMap.put("/register.shtml","anon");
        filterChainDefinitionMap.put("/login.shtml","anon");
        filterChainDefinitionMap.put("/sys/logout","anon");
        filterChainDefinitionMap.put("/sys/login","anon");
        filterChainDefinitionMap.put("/sys/register","anon");
        /**
         * 管理后台
         */
        filterChainDefinitionMap.put("/sys/**", "roles[admin]");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public SessionsSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //去掉小尾巴 JSESSIONID
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        long time = 2*60*60*1000;
        sessionManager.setGlobalSessionTimeout(time);
        return sessionManager;
    }
}
登录 LoginController:
/**
 * 登录
 */
@Controller
@RequestMapping("/sys")
public class LoginController {

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

    @Autowired
    private SysUserService sysUserService;

    /**
     * 登录
     */
    @PostMapping("/login")
    @ResponseBody
    public Result login(String username, String password){
        logger.info("用户登录");
        try{
            Subject subject = ShiroUtils.getSubject();
            password = MD5Utils.encrypt(username, password);
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            subject.login(token);
        }catch (Exception e) {
            e.printStackTrace();
            return Result.error("登录失败");
        }
        return Result.ok("登录成功");
    }
}
过滤器
shiro提供和多个默认的过滤器,我们可以用这些过滤器来配置过滤指定url的访问权限,这里只介绍几个比较常用的。
配置缩写 对应的过滤器 功能描述
anon AnonymousFilter 指定url可以匿名访问
logout LogoutFilter 登出过滤器,配置指定url就可以实现退出功能,非常方便
perms PermissionsAuthorizationFilter 需要指定权限才能访问
roles RolesAuthorizationFilter 需要指定角色才能访问
user UserFilter 需要已登录或“记住我”的用户才能访问
shiro常用的权限控制注解,可以在控制器类上使用。
注解 功能
@RequiresGuest 只有游客可以访问
@RequiresAuthentication 需要登录才能访问
@RequiresUser 已登录的用户或“记住我”的用户能访问
@RequiresRoles 已登录的用户需具有指定的角色才能访问
@RequiresPermissions 已登录的用户需具有指定的权限才能访问

前端使用

过滤器实现了后端的权限控制访问,那么如何在前端来使用注解实现按钮的显示呢。thymeleaf 是 SpringBoot 的标配模板了,这里我们使用第三方插件来实现。


pom.xml引入:
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>
ShiroConfig 追加以下代码:
@Bean
public ShiroDialect shiroDialect(){
    return new ShiroDialect();
}
页面头部引入:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
                xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
页面使用:
<!-- 堆代码 duidaima.com -->
<!-- 显示登陆用户昵称 -->
<shiro:principal property="nickname"/>

<!-- 游客 -->
<p shiro:guest="">Please <a href="login.html">login</a></p>

<!-- 认证通过或已记住的用户。 -->
<p shiro:user="">
Welcome back John! Not John? Click <a href="login.html">here</a> to login.
</p>

<!-- 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。 -->
<p shiro:authenticated="">
Hello, <span shiro:principal=""></span>, how are you today?
</p>

<a shiro:authenticated="" href="updateAccount.html">Update your contact information</a>

<!-- 输出当前用户信息,通常为登录帐号信息。 -->
<p>Hello, <shiro:principal/>, how are you today?</p>

<!-- 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。 -->
<p shiro:notAuthenticated="">
Please <a href="login.html">login</a> in order to update your credit card information.
</p>

<!-- 验证当前用户是否属于该角色。 -->
<a shiro:hasRole="admin" href="admin.html">Administer the system</a><!-- 拥有该角色 -->

<!-- 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。 -->
<p shiro:lacksRole="developer"><!-- 没有该角色 -->
Sorry, you are not allowed to developer the system.
</p>

<!-- 验证当前用户是否属于以下所有角色。 -->
<p shiro:hasAllRoles="developer, 2"><!-- 角色与判断 -->
You are a developer and a admin.
</p>

<!-- 验证当前用户是否属于以下任意一个角色。 -->
<p shiro:hasAnyRoles="admin, vip, developer,1"><!-- 角色或判断 -->
You are a admin, vip, or developer.
</p>

<!--验证当前用户是否拥有指定权限。 -->
<a shiro:hasPermission="userInfo:add" href="createUser.html">添加用户</a><!-- 拥有权限 -->

<!-- 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。 -->
<p shiro:lacksPermission="userInfo:del"><!-- 没有权限 -->
Sorry, you are not allowed to delete user accounts.
</p>

<!-- 验证当前用户是否拥有以下所有角色。 -->
<p shiro:hasAllPermissions="userInfo:view, userInfo:add"><!-- 权限与判断 -->
You can see or add users.
</p>

<!-- 验证当前用户是否拥有以下任意一个权限。 -->
<p shiro:hasAnyPermissions="userInfo:view, userInfo:del"><!-- 权限或判断 -->
You can see or delete users.
</p>
<a shiro:hasPermission="pp" href="createUser.html">Create a new User</a>


用户评论