权限管理框架

之前看的视频笔记, 做个备份,以免想找的时候总找不到。 


Shiro:权限管理框架


权限管理:

         基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源

         权限管理包括用户身份认证和授权两部分,简称认证授权。对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。


认证:

就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。


授权:

所谓授权就是权限控制, 通过认证的用户我们给与它不同访问权限, 不同角色拥有不同的权限,进入到系统中看到的以及用到的资源也不一样。


基本的通用权限模型:

通常企业开发中将资源和权限表合并为一张权限表(也可以分开两张表,单独进行关联),如下:

主体(账号、密码)

资源(资源名称,访问地址)

权限(权限名称、资源id)

*(

或者合并为:

权限(权限名称、资源名称、资源访问地址)

)

角色(角色名称)

角色和权限关系(角色id、权限id)

主体和角色关系(主体id、角色id)


上图常被称为权限管理的通用模型,不过企业在开发中根据系统自身的特点还会对上图进行修改,但是用户、角色、权限、用户角色关系、角色权限关系是需要去理解的。



权限管理的解决方案2种:

1,基于url拦截

* 要写自定义的拦截器

基于url拦截是企业中常用的权限管理方法,实现思路是:将系统操作的每个url配置在权限表中,将权限对应到角色,将角色分配给用户,用户访问系统功能通过Filter进行过虑,过虑器获取到用户访问的url,只要访问的url是用户分配角色中的url则放行继续访问。


例:

用户身份认证拦截器:
使用springmvc拦截器对用户身份认证进行拦截,如果用户没有登陆则跳转到登陆页面,本功能也可以使用filter实现 。


public class LoginInterceptor implements HandlerInterceptor {


// 在进入controller方法之前执行
// 使用场景:比如身份认证校验拦截,用户权限拦截,如果拦截不放行,controller方法不再执行
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {


// 校验用户访问是否是公开资源地址(无需认证即可访问)
List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");


// 用户访问的url
String url = request.getRequestURI();
for (String open_url : open_urls) {
if (url.indexOf(open_url) >= 0) {
// 如果访问的是公开 地址则放行
return true;
}
}


// 校验用户身份是否认证通过
HttpSession session = request.getSession();
ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
if (activeUser != null) {
// 用户已经登陆认证,放行
return true;
}
// 跳转到登陆页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,
response);
return false;
}




用户授权拦截器:
使用springmvc拦截器对用户访问url进行拦截,如果用户访问的url没有分配权限则跳转到无权操作提示页面(refuse.jsp),本功能也可以使用filter实现。

public class PermissionInterceptor implements HandlerInterceptor {


// 在进入controller方法之前执行
// 使用场景:比如身份认证校验拦截,用户权限拦截,如果拦截不放行,controller方法不再执行
// 进入action方法前要执行
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// TODO Auto-generated method stub
// 用户访问地址:
String url = request.getRequestURI();


// 校验用户访问是否是公开资源地址(无需认证即可访问)
List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
// 用户访问的url
for (String open_url : open_urls) {
if (url.indexOf(open_url) >= 0) {
// 如果访问的是公开 地址则放行
return true;
}
}
//从 session获取用户公共访问地址(认证通过无需分配权限即可访问)
List<String> common_urls = ResourcesUtil.gekeyList("commonURL");
// 用户访问的url
for (String common_url : common_urls) {
if (url.indexOf(common_url) >= 0) {
// 如果访问的是公共地址则放行
return true;
}
}
// 从session获取用户权限信息


HttpSession session = request.getSession();


ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");


// 取出session中权限url
// 获取用户操作权限
List<SysPermission> permission_list = activeUser.getPermissions();
// 校验用户访问地址是否在用户权限范围内
for (SysPermission sysPermission : permission_list) {
String permission_url = sysPermission.getUrl();
if (url.contains(permission_url)) {
return true;
}
}


// 跳转到页面
request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(
request, response);
return false;
}




2, 利用权限框架shiro

shiro:

Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。

shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro。

 java领域中springsecurity(原名Acegi)也是一个开源的权限管理框架,但是spring security依赖spring运行,而shiro就相对独立,最主要是因为shiro使用简单、灵活,所以现在越来越多的用户选择shiro。

 


Shiro架构:




详解:

1 Subject
Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权


2 SecurityManager 
SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。


3 Authenticator
Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
4 Authorizer
Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。


5 realm
Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。


6 sessionManager
sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
7 SessionDAO
SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。
8 CacheManager
CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
9 Cryptography
Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。


Shiro与项目集成开发

1 加入shiro的 jar包

  shiro-spring-1.2.3.jar

  shiro-web-1.2.3.jar


2 web.xml添加shiro Filter


<!-- shiro过虑器,DelegatingFilterProx会从spring容器中找shiroFilter -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>




3 applicationContext-shiro.xml 


<!-- Shiro 的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- 如果没有认证将要跳转的登陆地址,http可访问的url,如果不在表单认证过虑器FormAuthenticationFilter中指定此地址就为身份认证地址 -->
<property name="loginUrl" value="/login.action" />
<!-- 没有权限跳转的地址 -->
<property name="unauthorizedUrl" value="/refuse.jsp" />
<!-- shiro拦截器配置 -->
<property name="filters">
<map>
<entry key="authc" value-ref="formAuthenticationFilter" />
</map>
</property>
<property name="filterChainDefinitions">
<value>
<!-- 必须通过身份认证方可访问,身份认 证的url必须和过虑器中指定的loginUrl一致 -->
/loginsubmit.action = authc
<!-- 退出拦截,请求logout.action执行退出操作 -->
/logout.action = logout
<!-- 无权访问页面 -->
/refuse.jsp = anon
<!-- roles[XX]表示有XX角色才可访问 -->
/item/list.action = roles[item],authc
/js/** anon
/images/** anon
/styles/** anon
<!-- user表示身份认证通过或通过记住我认证通过的可以访问 -->
/** = user
<!-- /**放在最下边,如果一个url有多个过虑器则多个过虑器中间用逗号分隔,如:/** = user,roles[admin] -->


</value>
</property>
</bean>




<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm" />

</bean>


<!-- 自定义 realm -->
<bean id="userRealm" class="cn.itcast.ssm.realm.CustomRealm1">
</bean>
<!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
<bean id="formAuthenticationFilter"
class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
<!-- 表单中账号的input名称 -->
<property name="usernameParam" value="usercode" />
<!-- 表单中密码的input名称 -->
<property name="passwordParam" value="password" />
<!-- <property name="rememberMeParam" value="rememberMe"/> -->
<!-- loginurl:用户登陆地址,此地址是可以http访问的url地址 -->
<property name="loginUrl" value="/loginsubmit.action" />
</bean>






securityManager:这个属性是必须的。
loginUrl:没有登录认证的用户请求将跳转到此地址,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。
unauthorizedUrl:没有权限默认跳转的页面。




4 使用shiro注解授权


在springmvc.xml中配置shiro注解支持,可在controller方法中使用shiro注解配置权限:


<!-- 开启aop,对类代理 -->
<aop:config proxy-target-class="true"></aop:config>
<!-- 开启shiro注解支持 -->
<bean
class="
org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>


修改Controller代码,在方法上添加授权注解,如下:


// 查询商品列表
@RequestMapping("/queryItem")
@RequiresPermissions("item:query")
public ModelAndView queryItem() throws Exception {


上边代码@RequiresPermissions("item:query")表示必须拥有“item:query”权限方可执行。
其它的方法参考示例添加注解,一边添加一边思考这比基于url拦截有什么好处。




5 自定义realm


此realm先不从数据库查询权限数据,当前需要先将shiro整合完成,在上边章节定义的realm基础上修改。


public class CustomRealm1 extends AuthorizingRealm {


@Autowired
private SysService sysService;


@Override
public String getName() {
return "customRealm";
}


// 支持什么类型的token
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}


// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {


// 从token中 获取用户身份信息
String username = (String) token.getPrincipal();
// 拿username从数据库中查询
// ....
// 如果查询不到则返回null
if (!username.equals("zhang")) {// 这里模拟查询不到
return null;
}


// 获取从数据库查询出来的用户密码
String password = "123";// 这里使用静态数据模拟。。

// 根据用户id从数据库取出菜单
//...先用静态数据
List<SysPermission> menus = new ArrayList<SysPermission>();;

SysPermission sysPermission_1 = new SysPermission();
sysPermission_1.setName("商品管理");
sysPermission_1.setUrl("/item/queryItem.action");
SysPermission sysPermission_2 = new SysPermission();
sysPermission_2.setName("用户管理");
sysPermission_2.setUrl("/user/query.action");

menus.add(sysPermission_1);
menus.add(sysPermission_2);

// 构建用户身体份信息
ActiveUser activeUser = new ActiveUser();
activeUser.setUserid(username);
activeUser.setUsername(username);
activeUser.setUsercode(username);
activeUser.setMenus(menus);


// 返回认证信息由父类AuthenticatingRealm进行认证
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
activeUser, password, getName());


return simpleAuthenticationInfo;
}


// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
// 获取身份信息
ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
//用户id
String userid = activeUser.getUserid();
// 根据用户id从数据库中查询权限数据
// ....这里使用静态数据模拟
List<String> permissions = new ArrayList<String>();
permissions.add("item:query");
permissions.add("item:update");


// 将权限信息封闭为AuthorizationInfo


SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (String permission : permissions) {
simpleAuthorizationInfo.addStringPermission(permission);
}


return simpleAuthorizationInfo;
}


}






6 登录
//用户登陆页面
@RequestMapping("/login")
public String login()throws Exception{
return "login";
}
// 用户登陆提交
@RequestMapping("/loginsubmit")
public String loginsubmit(Model model, HttpServletRequest request)
throws Exception {


// shiro在认证过程中出现错误后将异常类路径通过request返回
String exceptionClassName = (String) request
.getAttribute("shiroLoginFailure");
if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
throw new CustomException("账号不存在");
} else if (IncorrectCredentialsException.class.getName().equals(
exceptionClassName)) {
throw new CustomException("用户名/密码错误");
} else{
throw new Exception();//最终在异常处理器生成未知错误
}
}


7 首页


由于session由shiro管理,需要修改首页的controller方法:


//系统首页
@RequestMapping("/first")
public String first(Model model)throws Exception{

//主体
Subject subject = SecurityUtils.getSubject();
//身份
ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
model.addAttribute("activeUser", activeUser);
return "/first";
}




8 退出

由于使用shiro的sessionManager,不用开发退出功能,使用shiro的logout拦截器即可。


<!-- 退出拦截,请求logout.action执行退出操作 -->
/logout.action = logout



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值