Apache Shiro是一个功能强大且易于使用的Java安全框架,提供了认证,授权,加密,和会话管理
如同 Spring security 一样都是是一个权限安全框架,但是与Spring Security相比,在于他使用了和比较简洁易懂的认证和授权方式。
1、Subject :当前用户的操作
2、SecurityManager:用于管理所有的Subject
3、Realms:用于进行权限信息的验证
Subject:即当前用户,在权限管理的应用程序里往往需要知道谁能够操作什么,谁拥有操作该程序的权利,shiro中则需要通过Subject来提供基础的当前用户信息,Subject 不仅仅代表某个用户,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。
SecurityManager:即所有Subject的管理者,这是Shiro框架的核心组件,可以把他看做是一个Shiro框架的全局管理组件,用于调度各种Shiro框架的服务。
Realms:Realms则是用户的信息认证器和用户的权限人证器,我们需要自己来实现Realms来自定义的管理我们自己系统内部的权限规则。
在shiro的用户权限认证过程中其通过两个方法来实现:
1、Authentication:是验证用户身份的过程。
2、Authorization:是授权访问控制,用于对用户进行的操作进行人证授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
除了以上几个组件外,Shiro还有几个其他组件:
1、SessionManager :Shiro为任何应用提供了一个会话编程范式。
2、CacheManager :对Shiro的其他组件提供缓存支持。
图片转自:http://kdboy.iteye.com/blog/1154644
持久层框架:Hibernate4 这边我使用了Hibernate来对数据持久层进行操作
控制显示层框架:SpringMVC 这边我使用了SpringMVC实际开发中也可以是其他框架
数据库:MySql
准备好所需要的jar放到项目中。
首先需要四张表,分别为 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 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;
}
@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 getPermissionsByUserName(String username) {
System.out.println("调用");
User user = getUserByUserName(username);
if (user == null) {
return null;
}
List list = new ArrayList();
// System.out.println(user.getUserRoles().get(0).get);
for (UserRole userRole : user.getUserRoles()) {
Role role = userRole.getRole();
List 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;
}
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 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两个方法,用于获取用户权限和认证用户登录信息
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;
}
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";
}
}
/toLogin = authc
/home = authc, perms[/home]
loginUrl 用于配置登陆页
successUrl 用于配置登录成功后返回的页面,不过该参数只会在当登录页面中并没有任何返回页面时才会生效,否则会跳转到登录Controller中的指定页面。
unauthorizedUrl 用于配置没有权限访问页面时跳转的页面
filterChainDefinitions:apache shiro通过filterChainDefinitions参数来分配链接的过滤,资源过滤有常用的以下几个参数:
1、authc 表示需要认证的链接
2、perms[/url] 表示该链接需要拥有对应的资源/权限才能访问
3、roles[admin] 表示需要对应的角色才能访问
4、perms[admin:url] 表示需要对应角色的资源才能访问
user login
在数据库中添加一条用户、角色、以及权限数据,并且在关联表中添加一条关联数据:
在浏览器中访问: home页面 就会跳转到登录页面:
最后输入 账号密码 就会跳转到登录成功页面。
shiro jar:http://download.csdn.net/detail/swingpyzf/8766673
项目源码:github:https://github.com/swinglife/shiro_ex
因为工作的原因写博客和教程的时间越来越少了,所以今后的博客尽量简短明了,以实践使用为主,
同时也欢迎大家关注我的个人公众号来提相关问题,我会力所能及的回答。