Apache Shiro

目录:

1、What is Apache Shiro?

2、shiro demo: 身份认证使用textRealm

3、身份认证使用jdbcRealm

4、编程式授权

5、shiro + spring

1、What is Apache Shiro? <--返回目录

  10 Minute Tutorial on Apache Shiro:http://shiro.apache.org/10-minute-tutorial.html

  Apache Shiro is a powerful and easy to use Java security framework that offers developers an intuitive yet comprehensive solution to authentication, authorization, cryptography, and session management.

  Shiro:java安全框架,提供认证、授权、加密和回话管理。

  获取当前用户:

Subject currentUser = SecurityUtils.getSubject();

  获取用户session:

Session session = currentUser.getSession();
session.setAttribute( "someKey", "aValue" );

  用户认证:

if ( !currentUser.isAuthenticated() ) {
    //collect user principals and credentials in a gui specific manner
    //such as username/password html form, X509 certificate, OpenID, etc.
    //We'll use the username/password example here since it is the most common.
    UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
    //this is all you have to do to support 'remember me' (no config - built in!):
    token.setRememberMe(true);
    currentUser.login(token);
}

  登陆失败的处理:

try {
    currentUser.login( token );
    //if no exception, that's it, we're done!
} catch ( UnknownAccountException uae ) {
    //username wasn't in the system, show them an error message?
} catch ( IncorrectCredentialsException ice ) {
    //password didn't match, try again?
} catch ( LockedAccountException lae ) {
    //account for that username is locked - can't login.  Show them a message?
}
    ... more types exceptions to check if you want ...
} catch ( AuthenticationException ae ) {
    //unexpected condition - error?
}

  获取登陆用户的信息,比如用户名:

currentUser.getPrincipal()

  判断登陆用户是否有指定角色或权限:

if ( currentUser.hasRole( "schwartz" ) ) {
    log.info("May the Schwartz be with you!" );
} else {
    log.info( "Hello, mere mortal." );
}

if ( currentUser.isPermitted( "lightsaber:weild" ) ) {
    log.info("You may use a lightsaber ring.  Use it wisely.");
} else {
    log.info("Sorry, lightsaber rings are for schwartz masters only.");
}

  用户退出:

currentUser.logout();

2、shiro demo: 身份认证使用textRealm <--返回目录

  依赖:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.2.4</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.12</version>
</dependency>

  

  在classpath新建配置文件shiro.ini

[users]
java=123456

  测试类

package com.oy;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

public class HelleWorld {

    public static void main(String[] args) {
        // 读取配置文件,初始化SecurityManager工厂
        Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
        // 获取securityManager实例
        SecurityManager securityManager=factory.getInstance();
        // 把securityManager实例绑定到SecurityUtils
        SecurityUtils.setSecurityManager(securityManager);
        // 得到当前执行的用户
        Subject currentUser=SecurityUtils.getSubject();
        // 创建token令牌,用户名/密码
        UsernamePasswordToken token=new UsernamePasswordToken("java", "123456");
        try{
            // 身份认证
            currentUser.login(token);    
            System.out.println("身份认证成功!");
        }catch(AuthenticationException e){
            e.printStackTrace();
            System.out.println("身份认证失败!");
        }
        // 退出
        currentUser.logout();
    }
}

3、身份认证使用jdbcRealm <--返回目录

  依赖:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.2.4</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.12</version>
</dependency>

<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.37</version>
</dependency>

  数据库:

CREATE DATABASE IF NOT EXISTS `db_shiro` DEFAULT CHARACTER SET utf8;
USE `db_shiro`;
DROP TABLE IF EXISTS `users`;

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userName` varchar(20) DEFAULT NULL,
  `password` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

insert  into `users`(`id`,`userName`,`password`) values (1,'java','123456');

  在classpath新建配置文件jdbc_shiro.ini

[main]
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
dataSource.driverClass=com.mysql.jdbc.Driver
dataSource.jdbcUrl=jdbc:mysql://localhost:3306/db_shiro
dataSource.user=root
dataSource.password=123456
jdbcRealm.dataSource=$dataSource
securityManager.realms=$jdbcRealm

  测试代码:

public static void main(String[] args) {
    // 读取配置文件,初始化SecurityManager工厂
    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:jdbc_shiro.ini");
    // 获取securityManager实例
    SecurityManager securityManager = factory.getInstance();
    // 把securityManager实例绑定到SecurityUtils
    SecurityUtils.setSecurityManager(securityManager);
    // 得到当前执行的用户
    Subject currentUser = SecurityUtils.getSubject();
    // 创建token令牌,用户名/密码
    UsernamePasswordToken token = new UsernamePasswordToken("java", "123456");
    try {
        // 身份认证
        currentUser.login(token);
        System.out.println("身份认证成功!");
    } catch (AuthenticationException e) {
        e.printStackTrace();
        System.out.println("身份认证失败!");
    }

    // 退出
    currentUser.logout();
}

4、编程式授权 <--返回目录

  shiro_role.ini

[users]
java=123456,role1,role2
jack=123456,role1

  角色

@Test
public void testHasRole() {
    Subject currentUser = ShiroUtils.login("classpath:shiro_role.ini", "java", "123456");
    
    System.out.println(currentUser.hasRole("role1") ? "有role1这个角色" : "没有role1这个角色");
    
    boolean[] results = currentUser.hasRoles(Arrays.asList("role1", "role2", "role3"));
    System.out.println(results[0] ? "有role1这个角色" : "没有role1这个角色");
    System.out.println(results[1] ? "有role2这个角色" : "没有role2这个角色");
    System.out.println(results[2] ? "有role3这个角色" : "没有role3这个角色");
    
    System.out.println(currentUser.hasAllRoles(Arrays.asList("role1", "role2")) ? "role1,role2这两个角色都有"
            : "role1,role2这个两个角色不全有");

    currentUser.logout();
}

@Test
public void testCheckRole() {
    Subject currentUser = ShiroUtils.login("classpath:shiro_role.ini", "java", "123456");
    
    // checkRole,没有该角色报UnauthorizedException
    currentUser.checkRole("role1");
    currentUser.checkRoles(Arrays.asList("role1", "role2"));
    currentUser.checkRoles("role1", "role2", "role3");

    currentUser.logout();
}

  shiro_permission.ini

[users]
java=123456,role1,role2
jack=123,role1
[roles]
role1=user:select
role2=user:add,user:update,user:delete

  权限

@Test
public void testIsPermitted() {
    Subject currentUser = ShiroUtils.login("classpath:shiro_permission.ini", "java", "123456");

    System.out.println(currentUser.isPermitted("user:select") ? "有user:select这个权限" : "没有user:select这个权限");
    System.out.println(currentUser.isPermitted("user:update") ? "有user:update这个权限" : "没有user:update这个权限");
    boolean results[] = currentUser.isPermitted("user:select", "user:update", "user:delete");
    System.out.println(results[0] ? "有user:select这个权限" : "没有user:select这个权限");
    System.out.println(results[1] ? "有user:update这个权限" : "没有user:update这个权限");
    System.out.println(results[2] ? "有user:delete这个权限" : "没有user:delete这个权限");
    
    System.out.println(currentUser.isPermittedAll("user:select", "user:update") ? "有user:select,update这两个权限"
            : "user:select,update这两个权限不全有");

    currentUser.logout();
}

@Test
public void testCheckPermitted() {
    Subject currentUser = ShiroUtils.login("classpath:shiro_permission.ini", "java", "123456");
    
    currentUser.checkPermission("user:select");
    currentUser.checkPermissions("user:select", "user:update", "user:delete");
    currentUser.logout();
}

  ShiroUtils

package com.oy.common;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

public class ShiroUtils {

    public static Subject login(String configFile, String userName, String password) {
        // 读取配置文件,初始化SecurityManager工厂
        Factory<SecurityManager> factory = new IniSecurityManagerFactory(configFile);
        // 获取securityManager实例
        SecurityManager securityManager = factory.getInstance();
        // 把securityManager实例绑定到SecurityUtils
        SecurityUtils.setSecurityManager(securityManager);
        // 得到当前执行的用户
        Subject currentUser = SecurityUtils.getSubject();
        // 创建token令牌,用户名/密码
        UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
        try {
            // 身份认证
            currentUser.login(token);
            System.out.println("身份认证成功!");
        } catch (AuthenticationException e) {
            e.printStackTrace();
            System.out.println("身份认证失败!");
        }
        return currentUser;
    }
}

5、shiro + spring <--返回目录

  依赖:

<!-- 添加Servlet支持 -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.1</version>
</dependency>

<!-- 添加jstl支持 -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

<!-- 添加Spring支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.1.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.1.7.RELEASE</version>
</dependency>
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-tx</artifactId>
     <version>4.1.7.RELEASE</version>
    </dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.1.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.1.7.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>4.1.7.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.1.7.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.1.7.RELEASE</version>
</dependency>


<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.1.7.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.1.7.RELEASE</version>
</dependency>

  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.2.3</version>
</dependency>


<!-- 添加日志支持 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

<!-- 添加mybatis支持 -->
 <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.3.0</version>
</dependency>

<!-- jdbc驱动包  -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.37</version>
</dependency>

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.2.4</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.12</version>
</dependency>

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.2.4</version>
</dependency>

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.2.4</version>
</dependency>

  web.xml

<!-- shiro过滤器定义 -->
<filter>  
    <filter-name>shiroFilter</filter-name>  
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
<init-param>  
<!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->  
<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>


<!-- Spring配置文件 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 编码过滤器 -->
<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <async-supported>true</async-supported>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring监听器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- 添加对springmvc的支持 -->
<servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

  application.xml中shiro相关配置

<!-- 自定义Realm -->
<bean  class="com.java1234.realm.MyRealm"/>  

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

<!-- Shiro过滤器 -->
<bean  class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
    <!-- Shiro的核心安全接口,这个属性是必须的 -->  
    <property name="securityManager" ref="securityManager"/>
    <!-- 身份认证失败,则跳转到登录页面的配置 -->  
    <property name="loginUrl" value="/index.jsp"/>
    <!-- 权限认证失败,则跳转到指定页面 -->  
    <property name="unauthorizedUrl" value="/unauthor.jsp"/>  
    <!-- Shiro连接约束配置,即过滤链的定义 -->  
    <property name="filterChainDefinitions">  
        <value>  
            /login=anon
            /admin*=authc
            /student=roles[teacher]
            /teacher=perms["user:create"]
        </value>  
    </property>
</bean>  

<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->  
<bean  class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>  

<!-- 开启Shiro注解 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>  
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
  <property name="securityManager" ref="securityManager"/>  
</bean>  

  自定义Realm

public class MyRealm extends AuthorizingRealm{

    @Resource
    private UserService userService;
    
    /**
     * 验证当前登录的用户
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String userName=(String)token.getPrincipal();
        User user=userService.getByUserName(userName);
        if(user!=null){
            AuthenticationInfo authcInfo=new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),"xx");
            return authcInfo;
        }else{
            return null;                
        }
    }
    
    /**
     * 为当限前登录的用户授予角色和权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String userName=(String)principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.getRoles(userName));
        authorizationInfo.setStringPermissions(userService.getPermissions(userName));
        return authorizationInfo;
    }
}

  登陆

/**
 * 用户Controller
 * @author oy
 */
@Controller
@RequestMapping("/user")
public class UserController {

    /**
     * 用户登录
     * @param user
     * @param request
     * @return
     */
    @RequestMapping("/login")
    public String login(User user,HttpServletRequest request){
        Subject subject=SecurityUtils.getSubject();
        UsernamePasswordToken token=new UsernamePasswordToken(user.getUserName(), user.getPassword());
        try{
            subject.login(token);
            Session session=subject.getSession();
            System.out.println("sessionId:"+session.getId());
            System.out.println("sessionHost:"+session.getHost());
            System.out.println("sessionTimeout:"+session.getTimeout());
            session.setAttribute("info", "session的数据");
            return "redirect:/success.jsp";
        }catch(Exception e){
            e.printStackTrace();
            request.setAttribute("user", user);
            request.setAttribute("errorMsg", "用户名或密码错误!");
            return "index";
        }
    }
    
}