标签:attribute 公司 注意 比较 author width 组件 eth 会话管理
尊重原创:http://blog.csdn.net/donggua3694857/article/details/52157313
最近在学习Shiro,首先非常感谢开涛大神的《跟我学Shiro》系列,在我学习的过程中发挥了很大的指导作用。学习一个新的东西首先就是做一个demo,多看不如多敲,只有在实践中才能发现自己的欠缺,下面记录下来我整合shiro的过程。如果有不足之处,还望各位看官多多指出。
一、基本名词解释
Apache Shiro是一个强大易用的Java安全框架。它可以帮助我们完成:认证、授权、加密、会话管理、与Web集成、缓存、单点登录等等,而且它的API也十分简洁易用,所以现在有很多人都在使用它。它的基本能功能点如图所示:
从图上我们可以看出Shiro的四大核心功能:
Authentication(身份验证):简称为“登录”,即证明用户是谁。
Authorization(授权):访问控制的过程,即决定是否有权限去访问受保护的资源。
Session Management(会话管理):管理用户特定的会话,即使在非 Web 或 EJB 应用程序。
Cryptography(加密):通过使用加密算法保持数据安全
我们同时也可以看看Shiro的架构长什么样子:
同样的虚线框框圈着的是Shiro3大核心组件:
Subject :正与系统进行交互的人,或某一个第三方服务。所有 Subject 实例都被绑定到(且这是必须的)一个SecurityManager 上。
SecurityManager:Shiro 架构的心脏,用来协调内部各安全组件,管理内部组件实例,并通过它来提供安全管理的各种服务。当 Shiro 与一个 Subject 进行交互时,实质上是幕后的 SecurityManager 处理所有繁重的 Subject 安全操作。
Realms :本质上是一个特定安全的 DAO。当配置 Shiro 时,必须指定至少一个 Realm 用来进行身份验证和/或授权。Shiro 提供了多种可用的 Realms 来获取安全相关的数据。如关系数据库(JDBC),INI 及属性文件等。可以定义自己 Realm 实现来代表自定义的数据源。
以上是些基本的名称解释。如需要查看更详细的请参考开涛大神的博客。
二、准备工作
整合程序沿用之前的例子Maven+spring+Spring MVC+MyBatis+MySQL整合SSM框架,现在我们需要在此基础上继续整合进Shiro。
三、开始整合
1.加入jar包
整合demo用的是maven对依赖进行管理。我们需要在pom.xml里加上配置:
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>1.1.32</version>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-core</artifactId>
- <version>1.2.2</version>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-web</artifactId>
- <version>1.2.2</version>
- </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>
里面我加入了阿里的fastjson。我用fastjson替换了jackson(理由:没有什么理由,就是喜欢用了.....)
2.Spring-mvc.xml的配置
这是替换掉jackson的配置
- <mvc:annotation-driven>
- <mvc:message-converters>
- <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
- <bean class="org.springframework.http.converter.FormHttpMessageConverter" />
- <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
-
- <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter" />
- </mvc:message-converters>
- </mvc:annotation-driven>
- <mvc:default-servlet-handler />
-
-
-
-
- <bean
- class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
- <property name="messageConverters">
- <list>
- <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
- <ref bean="fastJsonHttpMessageConverter" />
-
- </list>
- </property>
- </bean>
- <!--<bean id="mappingJacksonHttpMessageConverter"
- class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
- </bean>-->
-
- <bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
- <property name="supportedMediaTypes">
- <list>
- <value>text/html;charset=UTF-8</value>
- <value>application/json</value>
- </list>
- </property>
- <property name="features">
- <list>
- <value>WriteMapNullValue</value>
- <value>QuoteFieldNames</value>
- </list>
- </property>
- </bean>
3.首先先配置Shiro Filter(web.xml)
- <filter>
- <description>shiro 权限拦截</description>
- <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>
4.创建Shiro的配置文件spring-shiro(注:这边统一起名为spring-*.xml,然后可以在配置contextConfigLocation时使用通配符了)
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
-
- <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
-
- <property name="securityManager" ref="securityManager"/>
-
- <property name="loginUrl" value="/index.jsp"/>
-
- <property name="successUrl" value="/jsp/success.jsp"/>
- <span style="white-space:pre"> </span><property name="unauthorizedUrl" value="/jsp/unauthorized.jsp"/>
-
- <property name="filterChainDefinitions">
- <value>
- /index.jsp=anon
- /jsp/success.jsp=anon
- /jsp/fail.jsp=anon
- /jsp/user.jsp = roles[user]
- /jsp/admin.jsp = roles[admin]
- /logout = logout
- </value>
- </property>
- </bean>
-
-
- <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
-
- <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
- </bean>
-
- <bean id="myRealm" class="com.gray.base.shiro.ShiroDbRealm">
- <property name="userService" ref="userService" />
- </bean>
- <bean id="userService" class="com.gray.user.service.impl.UserServiceImpl" />
-
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
- <property name="cacheManager" ref="cacheManager"/>
- <property name="realm" ref="myRealm"/>
- <property name="sessionMode" value="native">
- </property>
- </bean>
-
- <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
- </beans>
5.数据库及页面设计好了。以上就是所有整合Shiro所需要配置的东西,下面我们开始写程序,为了方便,数据库我沿用之前的user表,将它进行了小小的改造,在实际项目中对角色和权限的控制需要在定义多张表的。helloworld程序就将就着用了。sql语句如下:CREATE TABLE `user` (`username` varchar(40) NOT NULL,`password` varchar(40) NOT NULL,`age` int(3) DEFAULT NULL,`name` varchar(40) DEFAULT NULL,`role` varchar(40) DEFAULT NULL,PRIMARY KEY (`username`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;表结构定义完后需要写实体类,这里就不贴代码了。jsp页面如图所示:
success.jsp在这里我加入了Shiro的标签来控制页面的显示。其他的jsp就不一一列出代码了。
- <%@ page language="java" contentType="text/html; charset=utf-8"pageEncoding="utf-8"%>
- <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
-
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <title></title>
- </head>
- <body>
- ${successMsg } Welcome! <shiro:principal/>
- <br><br>
-
- <shiro:hasAnyRoles name="user">
- <a href="/jsp/user.jsp">User Page</a>
- </shiro:hasAnyRoles>
-
- <br><br>
-
- <shiro:hasAnyRoles name="admin">
- <a href="/jsp/admin.jsp">Admin Page</a>
- </shiro:hasAnyRoles>
-
- <br><br>
- <a href="../test/logout.do">Logout</a>
- </body>
- </html>
6.后台代码的编写
6.1 Controller层的编写
这次整合我么主要要实现登陆和登出功能,因此controller层我们这样写:
- package com.gray.user.controller;
-
- import java.io.IOException;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
-
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.authc.AuthenticationException;
- import org.apache.shiro.authc.IncorrectCredentialsException;
- import org.apache.shiro.authc.UnknownAccountException;
- 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.ui.Model;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- import com.gray.user.entity.User;
- import com.gray.user.service.impl.UserServiceImpl;
-
- @Controller
- @RequestMapping("/test")
- public class LoginController {
- @Autowired
- private UserServiceImpl userService;
-
- @RequestMapping("/dologin.do")
- public String dologin(User user, Model model){
- String info = loginUser(user);
- if (!"SUCC".equals(info)) {
- model.addAttribute("failMsg", "用户不存在或密码错误!");
- return "/jsp/fail";
- }else{
- model.addAttribute("successMsg", "登陆成功!");
- model.addAttribute("name", user.getUsername());
- return "/jsp/success";
- }
- }
-
- @RequestMapping("/logout.do")
- public void logout(HttpServletRequest request,HttpServletResponse response) throws IOException{
- Subject subject = SecurityUtils.getSubject();
- if (subject != null) {
- try{
- subject.logout();
- }catch(Exception ex){
- }
- }
- response.sendRedirect("/index.jsp");
- }
-
- private String loginUser(User user) {
- if (isRelogin(user)) return "SUCC";
-
- return shiroLogin(user);
- }
- private String shiroLogin(User user) {
-
- UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword().toCharArray(), null);
- token.setRememberMe(true);
-
-
- try {
- SecurityUtils.getSubject().login(token);
- } catch (UnknownAccountException ex) {
- return "用户不存在或者密码错误!";
- } catch (IncorrectCredentialsException ex) {
- return "用户不存在或者密码错误!";
- } catch (AuthenticationException ex) {
- return ex.getMessage();
- } catch (Exception ex) {
- ex.printStackTrace();
- return "内部错误,请重试!";
- }
- return "SUCC";
- }
-
- private boolean isRelogin(User user) {
- Subject us = SecurityUtils.getSubject();
- if (us.isAuthenticated()) {
- return true;
- }
- return false;
- }
- }
6.2 实现自定义Realm
shiro从Realm获取安全数据,也就是说SecurityManager要验证身份,它需要从Realm获取相应的用户进行比较以确定用户的身份是否合法;我们可以把Realm看作是DataSource,安全数据源。实现自定义Realm主要是继承AuthrizingRealm这个父类,重写doGetAuthrizationInfo和doGetAuthenticationInfo这两个方法,其中doGetAuthenticationInfo是用来验证用户合法性的,根据输入的用户信息从数据库中查出用户,根据用户情况抛出不同的异常。doGetAuthrizationInfo是对当前用的用户进行授权的。在这里我把角色写死了。在实际项目开发中应该是在表中做权限和角色的定义再从数据库中查出一个集合然后迭代授权。具体实现代码如下:
- package com.gray.base.shiro;
-
- import org.apache.shiro.SecurityUtils;
- 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.session.Session;
- import org.apache.shiro.subject.PrincipalCollection;
- import org.springframework.beans.factory.annotation.Autowired;
-
- import com.gray.user.entity.User;
- import com.gray.user.service.impl.UserServiceImpl;
-
- public class ShiroDbRealm extends AuthorizingRealm {
- @Autowired
- private UserServiceImpl userService;
- public static final String SESSION_USER_KEY = "gray";
-
-
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
- User user = (User) SecurityUtils.getSubject().getSession().getAttribute(ShiroDbRealm.SESSION_USER_KEY);
- SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- info.addRole(user.getRole().trim());
- return info;
- }
-
-
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(
- AuthenticationToken authcToken) throws AuthenticationException {
-
- User userLogin = tokenToUser((UsernamePasswordToken) authcToken);
-
- User ui = userService.doUserLogin(userLogin);
- if(ui == null)
- return null;
-
- Session session = SecurityUtils.getSubject().getSession();
- session.setAttribute(ShiroDbRealm.SESSION_USER_KEY, ui);
-
- String realmName = this.getName();
-
- Object principal = authcToken.getPrincipal();
- return new SimpleAuthenticationInfo(principal, userLogin.getPassword(), realmName);
- }
-
- private User tokenToUser(UsernamePasswordToken authcToken) {
- User user = new User();
- user.setUsername(authcToken.getUsername());
- user.setPassword(String.valueOf(authcToken.getPassword()));
- return user;
- }
-
-
- public UserServiceImpl getUserService() {
- return userService;
- }
-
- public void setUserService(UserServiceImpl userService) {
- this.userService = userService;
- }
- }
在这里我们要注意的是用注解没办法注入我们要的service,一定要写getset方法,不然会报错(网上的解决办法挺有道理的但是试过没效果。。。。)。
具体的serviceImpl的实现我就不写出来了,数据库的查询我相信大家都会(默认你们都会)。随后如果有需要我会把整合代码放上来。
最后贴几张测试图:
登陆界面:
以管理员角色的账号登陆:
AdminPage:
以普通用户角色的账号登陆:
UserPage:
当你以普通用户登录却想访问adminpage时:
登陆失败:
最后登出点击logout返回登陆主界面。
以上就是整合Shiro的所有过程,当然在实际项目中不会那么简单但我觉得原理也是差不多的。学无止境,最后再次感谢网上提供资料的各位大神。
SpringMVC整合Shiro权限框架
标签:attribute 公司 注意 比较 author width 组件 eth 会话管理
原文地址:http://www.cnblogs.com/xiaohouzai/p/7795980.html