shiro权限控制详解编程语言

第一部分 什么是Apache Shiro

 
 

1、什么是 apache shiro :

 

Apache Shiro是一个功能强大且易于使用的Java安全框架,提供了认证,授权,加密,和会话管理

如同 spring security 一样都是是一个权限安全框架,但是与Spring Security相比,在于他使用了和比较简洁易懂的认证和授权方式。

 

 

 

2、Apache Shiro 的三大核心组件:

 

1、Subject :当前用户的操作

2、SecurityManager:用于管理所有的Subject

3、Realms:用于进行权限信息的验证

 

Subject:即当前用户,在权限管理的应用程序里往往需要知道谁能够操作什么,谁拥有操作该程序的权利,shiro中则需要通过Subject来提供基础的当前用户信息,Subject 不仅仅代表某个用户,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。

SecurityManager:即所有Subject的管理者,这是Shiro框架的核心组件,可以把他看做是一个Shiro框架的全局管理组件,用于调度各种Shiro框架的服务。

Realms:Realms则是用户的信息认证器和用户的权限人证器,我们需要自己来实现Realms来自定义的管理我们自己系统内部的权限规则。

 

 

 

3、Authentication 和 Authorization

 

在shiro的用户权限认证过程中其通过两个方法来实现:

1、Authentication:是验证用户身份的过程。

2、Authorization:是授权访问控制,用于对用户进行的操作进行人证授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。

 

 

 

4、其他组件:

 

除了以上几个组件外,Shiro还有几个其他组件:

1、SessionManager :Shiro为任何应用提供了一个会话编程范式。

2、CacheManager :对Shiro的其他组件提供缓存支持。 

 

 shiro权限控制详解编程语言

 

 

第二部分 Apache Shiro 整合Spring的Web程序构建

 

 

1、准备工具:

 

持久层框架:Hibernate4  这边我使用了hibernate来对数据持久层进行操作

控制显示层框架:SpringMVC 这边我使用了SpringMVC实际开发中也可以是其他框架

数据库MySQL

准备好所需要的jar放到项目中。

 

 

2、创建数据库:

 
 

首先需要四张表,分别为 user(用户)、role(角色)、permission(权限)、userRole(用户角色关系表)

这边分别创建四张表的实体类,通过Hiberante的hibernate.hbm2ddl.auto属性的update 来自动生成数据表结构。

 

/***  
 * 用户表  
 *   
 * @author Swinglife  
 *   
 */   
@Table(name = "t_user")   
@Entity   
public class User {   
   
    @Id   
    @GeneratedValue(strategy = GenerationType.AUTO)   
    Integer id;   
    /** 用户名 **/   
    String username;   
    /** 密码 **/   
    String password;   
    /** 是否删除 **/   
    Integer isDelete;   
    /** 创建时间 **/   
    Date createDate;   
    //多对多用户权限表   
    @OneToMany(mappedBy = "user",cascade=CascadeType.ALL)   
    List<UserRole> userRoles;   
   
省略get set….   
   
}  
/****  
 * 角色表  
 *   
 * @author Swinglife  
 *   
 */   
@Entity   
@Table(name = "t_role")   
public class Role {   
    @Id   
    @GeneratedValue(strategy = GenerationType.AUTO)   
    Integer id;   
    /**角色名**/   
    String name;   
    /**角色说明**/   
    String description;   
   
   
}  
/****  
 * 权限表  
 *   
 * @author Swinglife  
 *   
 */   
@Entity   
@Table(name = "t_permission")   
public class Permission {   
   
    @Id   
    @GeneratedValue(strategy = GenerationType.AUTO)   
    Integer id;   
    /**token**/   
    String token;   
    /**资源url**/   
    String url;   
    /**权限说明**/   
    String description;   
    /**所属角色编号**/   
    Integer roleId;   
   
}  
/***  
 * 用户角色表  
 *   
 * @author Swinglife  
 *   
 */   
@Entity   
@Table(name = "t_user_role")   
public class UserRole {   
   
    @Id   
    @GeneratedValue(strategy = GenerationType.AUTO)   
    Integer id;   
   
    @ManyToOne(cascade = CascadeType.ALL)   
    @JoinColumn(name = "userId", unique = true)   
    User user;   
    @ManyToOne   
    @JoinColumn(name = "roleId", unique = true)   
    Role role;   
   
}  

3、编写操作用户业务的Service:

 

@Service   
public class AccountService {   
   
    /****  
     * 通过用户名获取用户对象  
     *   
     * @param username  
     * @return  
     */   
    public User getUserByUserName(String username) {   
        User user = (User) dao.findObjectByHQL("FROM User WHERE username = ?", new Object[] { username });   
        return user;   
    }   
   
    /***  
     * 通过用户名获取权限资源  
     *   
     * @param username  
     * @return  
     */   
    public List<String> getPermissionsByUserName(String username) {   
        System.out.println("调用");   
        User user = getUserByUserName(username);   
        if (user == null) {   
            return null;   
        }   
        List<String> list = new ArrayList<String>();   
        // System.out.println(user.getUserRoles().get(0).get);   
        for (UserRole userRole : user.getUserRoles()) {   
            Role role = userRole.getRole();   
            List<Permission> permissions = dao.findAllByHQL("FROM Permission WHERE roleId = ?", new Object[] { role.getId() });   
            for (Permission p : permissions) {   
                list.add(p.getUrl());   
            }   
        }   
        return list;   
    }   
   
    // 公共的数据库访问接口   
    // 这里省略BaseDao dao的编写   
    @Autowired   
    private BaseDao dao;   
} 

4、编写shiro组件自定义Realm:

 

package org.swinglife.shiro;   
   
import java.util.List;   
   
import org.apache.shiro.authc.AuthenticationException;   
import org.apache.shiro.authc.AuthenticationInfo;   
import org.apache.shiro.authc.AuthenticationToken;   
import org.apache.shiro.authc.SimpleAuthenticationInfo;   
import org.apache.shiro.authc.UsernamePasswordToken;   
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.swinglife.model.User;   
import org.swinglife.service.AccountService;   
   
/****  
 * 自定义Realm  
 *   
 * @author Swinglife  
 *   
 */   
public class MyShiroRealm extends AuthorizingRealm {   
   
    /***  
     * 获取授权信息  
     */   
    @Override   
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {   
        //根据自己系统规则的需要编写获取授权信息,这里为了快速入门只获取了用户对应角色的资源url信息   
        String username = (String) pc.fromRealm(getName()).iterator().next();   
        if (username != null) {   
            List<String> pers = accountService.getPermissionsByUserName(username);   
            if (pers != null && !pers.isEmpty()) {   
                SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();   
                for (String each : pers) {   
                    //将权限资源添加到用户信息中   
                    info.addStringPermission(each);   
                }   
                return info;   
            }   
        }   
        return null;   
    }   
    /***  
     * 获取认证信息  
     */   
    @Override   
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken at) throws AuthenticationException {   
        UsernamePasswordToken token = (UsernamePasswordToken) at;   
        // 通过表单接收的用户名   
        String username = token.getUsername();   
        if (username != null && !"".equals(username)) {   
            User user = accountService.getUserByUserName(username);   
            if (user != null) {   
                return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());   
            }   
        }   
   
        return null;   
    }   
       
    /**用户的业务类**/   
    private AccountService accountService;   
       
    public AccountService getAccountService() {   
        return accountService;   
    }   
   
    public void setAccountService(AccountService accountService) {   
        this.accountService = accountService;   
    }   
   
}  

上述类继承了Shiro的AuthorizingRealm类 实现了AuthorizationInfo和AuthenticationInfo两个方法,用于获取用户权限和认证用户登录信息

 

 

5、编写LoginController:

 

package org.swinglife.controller;   
   
import org.apache.shiro.SecurityUtils;   
import org.apache.shiro.authc.UsernamePasswordToken;   
import org.apache.shiro.subject.Subject;   
import org.springframework.beans.factory.annotation.Autowired;   
import org.springframework.stereotype.Controller;   
import org.springframework.web.bind.annotation.RequestMapping;   
import org.springframework.web.bind.annotation.RequestMethod;   
import org.springframework.web.portlet.ModelAndView;   
import org.swinglife.model.User;   
import org.swinglife.service.AccountService;   
   
/****  
 * 用户登录Controller  
 *   
 * @author Swinglife  
 *   
 */   
@Controller   
public class LoginController {   
   
    /***  
     * 跳转到登录页面  
     *   
     * @return  
     */   
    @RequestMapping(value = "toLogin")   
    public String toLogin() {   
        // 跳转到/page/login.jsp页面   
        return "login";   
    }   
   
    /***  
     * 实现用户登录  
     *   
     * @param username  
     * @param password  
     * @return  
     */   
    @RequestMapping(value = "login")   
    public ModelAndView Login(String username, String password) {   
        ModelAndView mav = new ModelAndView();   
        User user = accountService.getUserByUserName(username);   
        if (user == null) {   
            mav.setView("toLogin");   
            mav.addObject("msg", "用户不存在");   
            return mav;   
        }   
        if (!user.getPassword().equals(password)) {   
            mav.setView("toLogin");   
            mav.addObject("msg", "账号密码错误");   
            return mav;   
        }   
        SecurityUtils.getSecurityManager().logout(SecurityUtils.getSubject());   
        // 登录后存放进shiro token   
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());   
        Subject subject = SecurityUtils.getSubject();   
        subject.login(token);   
        // 登录成功后会跳转到successUrl配置的链接,不用管下面返回的链接。   
        mav.setView("redirect:/home");   
        return mav;   
    }   
   
    // 处理用户业务类   
    @Autowired   
    private AccountService accountService;   
}  

6、编写信息认证成功后的跳转页面:

package org.swinglife.controller;   
   
import org.springframework.stereotype.Controller;   
import org.springframework.web.bind.annotation.RequestMapping;   
   
@Controller   
public class IndexController {   
   
    @RequestMapping("home")   
    public String index() {   
        System.out.println("登录成功");   
        return "home";   
    }   
} 

7、Shiro的配置文件.xml

 

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">   
        <property name="securityManager" ref="securityManager" />   
        <property name="loginUrl" value="/toLogin" />   
        <property name="successUrl" value="/home" />   
        <property name="unauthorizedUrl" value="/403" />   
            
        <property name="filterChainDefinitions">   
            <value>   
                /toLogin = authc <!-- authc 表示需要认证才能访问的页面 -->   
                /home = authc, perms[/home]  <!-- perms 表示需要该权限才能访问的页面 -->   
            </value>   
        </property>   
    </bean>   
   
   
   
   
    <bean id="myShiroRealm" class="org.swinglife.shiro.MyShiroRealm">   
        <property name="accountService" ref="accountService" />   
    </bean>   
   
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">   
        <property name="realm" ref="myShiroRealm"></property>   
    </bean>   
   
    <bean id="accountService" class="org.swinglife.service.AccountService"></bean>   
   
    <!-- <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">    
        <property name="cacheManager" ref="cacheManager" /> </bean> -->  

loginUrl 用于配置登陆页

successUrl 用于配置登录成功后返回的页面,不过该参数只会在当登录页面中并没有任何返回页面时才会生效,否则会跳转到登录Controller中的指定页面。

unauthorizedUrl 用于配置没有权限访问页面时跳转的页面

 

filterChainDefinitions:apache shiro通过filterChainDefinitions参数来分配链接的过滤,资源过滤有常用的以下几个参数:

1、authc 表示需要认证的链接

2、perms[/url] 表示该链接需要拥有对应的资源/权限才能访问

3、roles[admin] 表示需要对应的角色才能访问

4、perms[admin:url] 表示需要对应角色的资源才能访问

 

8、登陆页login.jsp

 

<body>   
   
<h1>user login</h1>   
<form action="login" method="post">   
username:<input type="text" name="username"><p>   
password:<input type="password" name="password">   
<p>   
${msg }   
<input type="submit" value="submit">   
</form>   
</body> 

9、运行程序

 

在数据库中添加一条用户、角色、以及权限数据,并且在关联表中添加一条关联数据:

 

shiro权限控制详解编程语言

 

在浏览器中访问: home页面 就会跳转到登录页面:  

 

shiro权限控制详解编程语言

 

最后输入 账号密码 就会跳转到登录成功页面。

关于如何实现同一时刻只有一个相同账户登录的问题:

  DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager(); 
            DefaultWebSessionManager sessionManager = (DefaultWebSessionManager)securityManager.getSessionManager(); 
            Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();

通过以上代码得到所有的session,循环遍历session:

    for(Session session:sessions){ 
 
        System.out.println("登录ip:"+session.getHost()); 
 
        System.out.println("登录用户"+session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY)); 
 
        System.out.println("最后操作日期:"+session.getLastAccessTime()); 
 
        }

然后自行判断是否存在某一个账户,如果不存在可以登录,如果存在return null

 

如果只进入了doGetAuthenticationInfo方法,没有进入doGetAuthorizationInfo方法,并且配置无异常,无遗漏,可能是因为在过滤器中权限配置没有配置到你即将进入的页面,如果没有配置perms[],则shiro不会自行进入doGetAuthorizationInfo方法进行权限判断。

 

shiro jar:http://download.csdn.net/detail/swingpyzf/8766673

项目源码:github:https://github.com/swinglife/shiro_ex

本文转自:http://blog.csdn.net/swingpyzf/article/details/46342023/

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/11504.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论