shiro权限认证框架学习

shiro与spring Secuity比较:

1 简单灵活

2 科托离spring

3 粒度较粗

spring Secuity与之相反,同时它也是又shiro开发

shiro认证过程:

1  创建secrityManger

2 主体提交认证

3 securityManger认证

4 Realm验证

5 Authenticor认证

代码:

package com.imooc.test;


import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

/**认证
 * @author fusiping
 * 2018年11月23日
 */
public class AuthenticationTest {

	SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
	
	@Before
	public void addUser() {
		simpleAccountRealm.addAccount("Mark","123456");
	}
	
	@Test
	public  void testAuthentication() {
		//1.构建securityManage环境
		DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
		defaultSecurityManager.setRealm(simpleAccountRealm);
		
		//设置secutityManager环境
		SecurityUtils.setSecurityManager(defaultSecurityManager);
		//2.主体提交认证请求
		Subject subject = SecurityUtils.getSubject();
		
		UsernamePasswordToken token =  new UsernamePasswordToken("Mark","123456");
		
		subject.login(token);
		//结果:isAuthenticated:true
		System.out.println("isAuthenticated:"+subject.isAuthenticated());
		
		subject.logout();
		//结果:isAuthenticated:false
		System.out.println("isAuthenticated:"+subject.isAuthenticated());
		
	}
}

shiro授权:

1 创建SecurityManage

2 主体授权

3 securitymanager授权

4 authorizer授权

5 Realm获取角色权限数据

代码:

package com.imooc.test;


import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

/**认证
 * @author fusiping
 * 2018年11月23日
 */
public class AuthenticationTest {

	SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
	
	@Before
	public void addUser() {
		simpleAccountRealm.addAccount("Mark","123456","admin");
	}
	
	/**
	 * 授权
	 */
	@Test
	public  void testAuthorizer() {
		//1.构建securityManage环境
		DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
		defaultSecurityManager.setRealm(simpleAccountRealm);
		
		//设置secutityManager环境
		SecurityUtils.setSecurityManager(defaultSecurityManager);
		//2.主体提交认证请求
		Subject subject = SecurityUtils.getSubject();
		
		UsernamePasswordToken token =  new UsernamePasswordToken("Mark","123456");
		subject.login(token);
		
		//结果:isAuthenticated:true
		System.out.println("isAuthenticated:"+subject.isAuthenticated());
		
		//没有抛出异常,说明验证成功
		subject.checkRole("admin");
		
	}
	
	
}

shiro自定义Realm

内置Realm:IniRealm,JdbcRealm

package com.imooc.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

public class IniRealmTest {

    @Test
    public  void testIniRealmr() {
        //自定义
        IniRealm iniRealm =  new IniRealm("classpath:user.ini");
        //1.构建securityManage环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(iniRealm);
        //设置secutityManager环境
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //2.主体提交认证请求
        Subject subject = SecurityUtils.getSubject();
        
        UsernamePasswordToken token =  new UsernamePasswordToken("Mark","123456");
        subject.login(token);
        
        //结果:isAuthenticated:true
        System.out.println("isAuthenticated:"+subject.isAuthenticated());
        
        subject.checkRole("user1");
        subject.checkPermission("user:update");
    }
}

user.ini文件:

[users]
Mark=123456,admin,user1
[roles]
admin=user:delete
user1=user:update

jdbcRealm实例:

package com.imooc.test;

import javax.swing.JScrollBar;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

import com.alibaba.druid.pool.DruidDataSource;

public class JdbcRealmTest {
    
    DruidDataSource dataSource = new DruidDataSource();
    {
        dataSource.setUrl("jdbc:mysql://localhost:3333/test1");
        dataSource.setUsername("test1");
        dataSource.setPassword("fsp648734!");
    }

    @Test
    public  void testIniRealmr() {
        
        JdbcRealm jdbcRealm = new JdbcRealm();
        jdbcRealm.setDataSource(dataSource);
        //使用jdbcRealm必须设置开关为true,默认为false,否则不能查询权限数据
        jdbcRealm.setPermissionsLookupEnabled(true);
        //自定义认证查询语句
        //jdbcRealm.setAuthenticationQuery("authenticationQuery");
        //自定义权限查询语句
        //jdbcRealm.setPermissionsQuery("permissionsQuery");
        //自定义角色查询语句
        //jdbcRealm.setUserRolesQuery("userRolesQuery");
        //1.构建securityManage环境
        /*
         * 默认的查询语句:
        protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";
        
        protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?";

        protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";
*
        protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";*/
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(jdbcRealm);
        //设置secutityManager环境
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //2.主体提交认证请求
        Subject subject = SecurityUtils.getSubject();
        
        UsernamePasswordToken token =  new UsernamePasswordToken("fusiping","123456");
        subject.login(token);
        
        //结果:isAuthenticated:true
        System.out.println("isAuthenticated:"+subject.isAuthenticated());
        
        subject.checkRole("admin");
        subject.checkPermission("user:delete");
    }
}
 

默认的sql语句:

对应的数据库表:

CREATE TABLE `roles_permissions` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  `permission` varchar(32) NOT NULL COMMENT '权限',
  `role_name` varchar(64) NOT NULL COMMENT '角色',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色-权限';

CREATE TABLE `user_roles` (
  `id` int(11) NOT NULL COMMENT '编号',
  `username` varchar(255) NOT NULL COMMENT '用户名',
  `role_name` varchar(255) NOT NULL COMMENT '角色',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='shiro用户表';


CREATE TABLE `users` (
  `id` int(8) NOT NULL AUTO_INCREMENT COMMENT '主键自增',
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(50) NOT NULL COMMENT '密码',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT='用户表';

自定义Realm:

package com.imooc.shrio.realm;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**自定义
 * @author fusiping
 * 2018年11月23日
 */
public class CustomRealm extends AuthorizingRealm{
    
    //模拟数据库查询
    Map<String, String> map = new HashMap<String, String>(16);
    {
        map.put("fusiping", "123456");
        super.setName("customRealm");
    }

    /* 用来做授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String userName = (String) principals.getPrimaryPrincipal();
        //从数据库或者从缓存中获取角色数据
        Set<String> roles = getRolesByUserName(userName);
        //通过用户从从数据库或者从缓存中获取权限数据
        Set<String> permissions = getPermissionsByUserName(userName);
        SimpleAuthorizationInfo simpleAuthenticationInfo = new SimpleAuthorizationInfo();
        simpleAuthenticationInfo.setStringPermissions(permissions);
        simpleAuthenticationInfo.setRoles(roles);
        return simpleAuthenticationInfo;
    }

    /**模拟从数据库或缓存中获取权限数据
     * @param userName
     * @return
     */
    private Set<String> getPermissionsByUserName(String userName) {
        Set<String> permissions = new HashSet<String>();
        permissions.add("user:delete");
        permissions.add("user:add");
        return permissions;
    }

    /**模拟数据查询
     * @param userName
     * @return
     */
    private Set<String> getRolesByUserName(String userName) {
        Set<String> roles = new HashSet<String>();
        roles.add("admin");
        roles.add("user");
        return roles;
    }

    /* 用来做认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //1.从主体传过来的认证信息中,获取用户名
        String userName = (String) token.getPrincipal();
        
        //2.通过用户名找到数据库中获取凭证
        String password = getPasswordByUserName(userName);
        if (password == null) {
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("fusiping",password,"customRealm");
        return authenticationInfo;
    }

    /**模拟数据库查询凭证
     * @param userName
     * @return
     */
    private String getPasswordByUserName(String userName) {
        return map.get(userName);
    }
    
    
}
 

 测试类CustomRealmTest :

package com.imooc.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

import com.imooc.shrio.realm.CustomRealm;

/**测试
 * @author fusiping
 * 2018年11月23日
 */
public class CustomRealmTest {

    @Test
    public  void testCustomRealm() {
        //自定义
        CustomRealm customRealm = new CustomRealm();
        //1.构建securityManage环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(customRealm);
        //设置secutityManager环境
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //2.主体提交认证请求
        Subject subject = SecurityUtils.getSubject();
        
        UsernamePasswordToken token =  new UsernamePasswordToken("fusiping","123456");
        subject.login(token);
        
        //结果:isAuthenticated:true
        System.out.println("isAuthenticated:"+subject.isAuthenticated());
        
        subject.checkRole("admin");
        subject.checkPermission("user:delete");
    }
}
 

 

shiro加密:

shiro散列配置:

1 hashedCreadentialsMatcher

2 自定义Realm中使用散列

3 盐的使用 

package com.imooc.shrio.realm;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

/**自定义
 * @author fusiping
 * 2018年11月23日
 */
public class CustomRealm extends AuthorizingRealm{
    
    //模拟数据库查询
    Map<String, String> map = new HashMap<String, String>(16);
    {
        map.put("fusiping", "cc2bf701edeca9268997bab3f5b6527d");//123456,Md5加盐“fusiping”,加密后e10adc3949ba59abbe56e057f20f883e
        super.setName("customRealm");
    }

    /* 用来做授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String userName = (String) principals.getPrimaryPrincipal();
        //从数据库或者从缓存中获取角色数据
        Set<String> roles = getRolesByUserName(userName);
        //通过用户从从数据库或者从缓存中获取权限数据
        Set<String> permissions = getPermissionsByUserName(userName);
        SimpleAuthorizationInfo simpleAuthenticationInfo = new SimpleAuthorizationInfo();
        simpleAuthenticationInfo.setStringPermissions(permissions);
        simpleAuthenticationInfo.setRoles(roles);
        return simpleAuthenticationInfo;
    }

    /**模拟从数据库或缓存中获取权限数据
     * @param userName
     * @return
     */
    private Set<String> getPermissionsByUserName(String userName) {
        Set<String> permissions = new HashSet<String>();
        permissions.add("user:delete");
        permissions.add("user:add");
        return permissions;
    }

    /**模拟数据查询
     * @param userName
     * @return
     */
    private Set<String> getRolesByUserName(String userName) {
        Set<String> roles = new HashSet<String>();
        roles.add("admin");
        roles.add("user");
        return roles;
    }

    /* 用来做认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //1.从主体传过来的认证信息中,获取用户名
        String userName = (String) token.getPrincipal();
        
        //2.通过用户名找到数据库中获取凭证
        String password = getPasswordByUserName(userName);
        if (password == null) {
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("fusiping",password,"customRealm");
        //识别加盐
        authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("fusiping"));
        return authenticationInfo;
    }

    /**模拟数据库查询凭证
     * @param userName
     * @return
     */
    private String getPasswordByUserName(String userName) {
        return map.get(userName);
    }
    
    public static void main(String[] args) {
        //加盐:“fusiping”
        Md5Hash md5Hash = new Md5Hash("123456","fusiping");
        System.out.println(md5Hash.toString());
    }
    
}
 

测试:

package com.imooc.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

import com.imooc.shrio.realm.CustomRealm;

/**测试
 * @author fusiping
 * 2018年11月23日
 */
public class CustomRealmTest {

    @Test
    public  void testCustomRealm() {
        //自定义
        CustomRealm customRealm = new CustomRealm();
        //1.构建securityManage环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(customRealm);
        
        //加密
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("md5");
        //加密次数
        matcher.setHashIterations(1);
        customRealm.setCredentialsMatcher(matcher);
        
        //设置secutityManager环境
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //2.主体提交认证请求
        Subject subject = SecurityUtils.getSubject();
        
        UsernamePasswordToken token =  new UsernamePasswordToken("fusiping","123456");
        subject.login(token);
        
        //结果:isAuthenticated:true
        System.out.println("isAuthenticated:"+subject.isAuthenticated());
        
        subject.checkRole("admin");
        subject.checkPermission("user:delete");
    }
}
 

shiro+spring整合:

package com.fsp.shiro.realm;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

public class CustomRealm extends AuthorizingRealm {

	//模拟数据库查询
		Map<String, String> map = new HashMap<String, String>(16);
		{
			map.put("fusiping", "cc2bf701edeca9268997bab3f5b6527d");//123456,Md5加盐“fusiping”,加密后e10adc3949ba59abbe56e057f20f883e
			super.setName("customRealm");
		}

		/* 用来做授权
		 * @param principals
		 * @return
		 */
		@Override
		protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
			String userName = (String) principals.getPrimaryPrincipal();
			//从数据库或者从缓存中获取角色数据
			Set<String> roles = getRolesByUserName(userName);
			//通过用户从从数据库或者从缓存中获取权限数据
			Set<String> permissions = getPermissionsByUserName(userName);
			SimpleAuthorizationInfo simpleAuthenticationInfo = new SimpleAuthorizationInfo();
			simpleAuthenticationInfo.setStringPermissions(permissions);
			simpleAuthenticationInfo.setRoles(roles);
			return simpleAuthenticationInfo;
		}

		/**模拟从数据库或缓存中获取权限数据
		 * @param userName
		 * @return
		 */
		private Set<String> getPermissionsByUserName(String userName) {
			Set<String> permissions = new HashSet<String>();
			permissions.add("user:delete");
			permissions.add("user:add");
			return permissions;
		}

		/**模拟数据查询
		 * @param userName
		 * @return
		 */
		private Set<String> getRolesByUserName(String userName) {
			Set<String> roles = new HashSet<String>();
			roles.add("admin");
			roles.add("user");
			return roles;
		}

		/* 用来做认证
		 * @param token
		 * @return
		 * @throws AuthenticationException
		 */
		@Override
		protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

			//1.从主体传过来的认证信息中,获取用户名
			String userName = (String) token.getPrincipal();
			
			//2.通过用户名找到数据库中获取凭证
			String password = getPasswordByUserName(userName);
			if (password == null) {
				return null;
			}
			SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("fusiping",password,"customRealm");
			//识别加盐
			authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("fusiping"));
			return authenticationInfo;
		}

		/**模拟数据库查询凭证
		 * @param userName
		 * @return
		 */
		private String getPasswordByUserName(String userName) {
			return map.get(userName);
		}
}

controller:

package com.fsp.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fsp.controller.req.UserModel;

@Controller
public class UserController {

	@RequestMapping(value="/subLogin",method = RequestMethod.POST,produces = "application/json;charset=utf-8")
	@ResponseBody
	public String subLogin(UserModel userModel) {
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken(userModel.getUsername(),userModel.getPassword());
		try {
			subject.login(token);
		} catch (AuthenticationException e) {
			return "登陆异常:"+e.getMessage();
		}
		
		return "登陆成功";
	}
	
	
}

 userModel:

package com.fsp.controller.req;

import lombok.Getter;
import lombok.Setter;

/**登陆接收model
 * @author fusiping
 * 2018年11月23日
 */
@Getter
@Setter
public class UserModel {
	
	private String username;
	private String password;
	

}

 配置文件:

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="
            http://java.sun.com/xml/ns/javaee
            http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </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*:spring/spring.xml</param-value>
      </context-param>
      <!-- 对spring开始监听 -->
      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      
      <listener>
          <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
      </listener>
    
    <servlet>
        <servlet-name>springServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:spring/springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

spring.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.0.xsd"
    default-lazy-init="true">

    <description>Shiro Configuration</description>
    
    <!-- 安全认证过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="login.html" />
        <property name="unauthorizedUrl" value="403.html"/>
        <property name="filterChainDefinitions">
            <ref bean="shiroFilterChainDefinitions"/>
        </property>
    </bean>
    
    <!-- Shiro权限过滤过滤器定义 -->
    <bean name="shiroFilterChainDefinitions" class="java.lang.String">
        <constructor-arg>
            <value>
                /login.html = anon
                /subLogin = anon
                /subLogin2 = anon
                /* = authc
            </value>
        </constructor-arg>
    </bean>
    
    <!--   定义Shiro安全管理配置 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="realm" />
    </bean>
    
    <bean class="com.fsp.shiro.realm.CustomRealm" id="realm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>
    
    <!-- 加密对象-->
    <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" id="credentialsMatcher">
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="1"/>
    </bean>

 </beans>

springmvc.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/mvc  http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"
    default-lazy-init="true">
    
    <context:component-scan base-package="com.fsp.controller"/>
    <mvc:annotation-driven/>
    <mvc:resources  mapping="/*" location="/"/>
    </beans> 

login.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="subLogin" method="post">
    用户名:<input type="text" name="username"/><br>
    密码:<input type="password" name="password"/><br>
    <input type="submit" value="登陆">
</form>

</body>
</html>

shiro集成spring从数据库获取数据

spring-dao.xml:

<?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-4.0.xsd">
    
   <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
           <property name="url" value="jdbc:mysql://localhost:3333/test1"/>
           <property name="username" value="test1"/>
           <property name="password" value="fsp648734!"/>
   </bean>
   
   <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
           <property name="dataSource" ref="dataSource"/>
   </bean>
</beans>

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.0.xsd"
    default-lazy-init="true">

    <import resource="spring-dao.xml"/>
    <context:component-scan base-package="com.fsp"/>
    
    <!-- 安全认证过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="login.html" />
        <property name="unauthorizedUrl" value="403.html"/>
        <!-- 
        <property name="filters">
            <map>
                <entry key="cas" value-ref="casFilter"/>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property> -->
        <property name="filterChainDefinitions">
            <ref bean="shiroFilterChainDefinitions"/>
        </property>
    </bean>
    
    <!-- Shiro权限过滤过滤器定义 -->
    <bean name="shiroFilterChainDefinitions" class="java.lang.String">
        <constructor-arg>
            <value>
                /login.html = anon
                /subLogin = anon
                /* = authc
            </value>
        </constructor-arg>
    </bean>
    
    <!--   定义Shiro安全管理配置 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="realm" />
    </bean>
    
    <bean class="com.fsp.shiro.realm.CustomRealm" id="realm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>
    
    <!-- 加密对象-->
    <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" id="credentialsMatcher">
        <property name="hashAlgorithmName" value="MD5"/>
        <property name="hashIterations" value="1"/>
    </bean>

</beans>

CustomRealm:

package com.fsp.shiro.realm;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.fsp.dao.UserDao;
import com.fsp.entity.User;

@Component
public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private UserDao userDao;

    /* 用来做授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String userName = (String) principals.getPrimaryPrincipal();
        //从数据库或者从缓存中获取角色数据
        Set<String> roles = getRolesByUserName(userName);
        //通过用户从从数据库或者从缓存中获取权限数据
        Set<String> permissions = new LinkedHashSet<String>();
        for (String role : roles) {
            Set<String> list = getPermissionsByUserName(role);
            permissions.addAll(list);
        }
        SimpleAuthorizationInfo simpleAuthenticationInfo = new SimpleAuthorizationInfo();
        simpleAuthenticationInfo.setStringPermissions(permissions);
        simpleAuthenticationInfo.setRoles(roles);
        return simpleAuthenticationInfo;
    }

    /**模拟从数据库或缓存中获取权限数据
     * @param userName
     * @return
     */
    private Set<String> getPermissionsByUserName(String roleName) {
        List<String> list = userDao.queryPermissionsByUserName(roleName);
        Set<String> permissions = new HashSet<String>(list);
        return permissions;
    }

    /**数据查询角色
     * @param userName
     * @return
     */
    private Set<String> getRolesByUserName(String userName) {
        List<String> list = userDao.queryRolesByUserName(userName);
        Set<String> roles = new HashSet<String>(list);
        return roles;
    }

    /* 用来做认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //1.从主体传过来的认证信息中,获取用户名
        String userName = (String) token.getPrincipal();
        
        //2.通过用户名找到数据库中获取凭证
        String password = getPasswordByUserName(userName);
        if (password == null) {
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName,password,"customRealm");
        //识别加盐,盐值随意
        authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(userName));
        return authenticationInfo;
    }

    /*public static void main(String[] args) {
        //数据源+盐
        Md5Hash md5 = new Md5Hash("123456","fusiping");
        System.out.println(md5.toString());
    }*/
    /**模拟数据库查询凭证
     * @param userName
     * @return
     */
    private String getPasswordByUserName(String userName) {
        User user = userDao.getUserByUserName(userName);
        if (user != null) {
            return user.getPassword();
        }
        return null;
    }
}
 

dao:

package com.fsp.dao;

import java.util.List;

import com.fsp.entity.User;

public interface UserDao {

    User getUserByUserName(String userName);

    List<String> queryRolesByUserName(String userName);

    List<String> queryPermissionsByUserName(String roleName);

}
 

daoImpl:

 

package com.fsp.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;

import com.fsp.dao.UserDao;
import com.fsp.entity.User;

@Component
public class UserDaoImpl implements UserDao{

    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    /* 数据库查询密码
     * @param userName
     * @return
     */
    public User getUserByUserName(String userName) {
        String sql = "select username, password from users where username = ?";
        List<User> list = jdbcTemplate.query(sql, new String[] {userName}, new RowMapper<User>() {
            public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                User user = new User();
                user.setUsername(rs.getString("username"));
                user.setPassword(rs.getString("password"));
                return user;
            }
        });
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        return list.get(0);
    }

    /* 数据库查询角色
     * @param userName
     * @return
     */
    public List<String> queryRolesByUserName(String userName) {
        String sql = "select role_name from user_roles where username = ?";
        return jdbcTemplate.query(sql, new String[] {userName}, new RowMapper<String>() {
            public String mapRow(ResultSet rs, int rowNum) throws SQLException {
                return rs.getString("role_name");
            }
        });
    }

    /* 根据角色返回权限数据
     * @param roleName
     * @return
     */
    public List<String> queryPermissionsByUserName(String roleName) {
        String sql = "select permission from roles_permissions where role_name = ?";
        return jdbcTemplate.query(sql, new String[] {roleName}, new RowMapper<String>() {
            public String mapRow(ResultSet rs, int rowNum) throws SQLException {
                return rs.getString("permission");
            }
        });
    }

}
 

controller:

package com.fsp.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fsp.controller.req.UserModel;

@Controller
public class UserController {

    @RequestMapping(value="/subLogin",method = RequestMethod.POST,produces = "application/json;charset=utf-8")
    @ResponseBody
    public String subLogin(UserModel userModel) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(userModel.getUsername(),userModel.getPassword());
        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            return "登陆异常:"+e.getMessage();
        }
        
        if (subject.hasRole("admin")) {
            subject.checkPermission("user:delete");
            return "有admin权限";
            
        }
        return "无admin权限";
    }
    
    
}
 

通过注解配置权限:

pom.xml添加:

    <!-- 开启注解方式授权shiro -->
      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.8.9</version>
      </dependency>

springmvc.xml添加:

 
    <!-- 开启注解方式shrio需要的配置 -->
    <aop:config proxy-target-class="true"/>
    <bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean> 

controller添加:

/**当前主题必须具备amdin觉得才可以访问
     * @return
     */
    @RequiresRoles("admin")
    @RequestMapping(value="/testRole",method=RequestMethod.GET)
    @ResponseBody
    public String testRole() {
        return "testRole success";
    }
    
    
    /**推荐使用的方法
     * @return
     */
    @RequiresPermissions("user:delete")
    @RequestMapping(value="/testRole1",method=RequestMethod.GET)
    @ResponseBody
    public String testRole1() {
        return "testRole success";
    }

shrio内置过滤器:

认证过滤器:anon,authBasic,authc,user,logout 

anon:不需要任何认证,直接访问

authBasic:HTTP基本认证

authc:需要认证之后才可以访问

user:需要当前存在用户才能访问

logout;退出、

授权过滤器:perms,roles,ssl,port

perms:需要具备相关权限才可以访问

roles:跟perms差不多,在[]里面满足的相关权限

ssl:https协议

port:[]里面的端口

 

 

自定义filter:

package com.fsp.filter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

/**自动一授权,如果包含其中一个权限,则执行
 * @author fusiping
 * 2018年11月27日
 */
public class RoleOrFilter extends AuthorizationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
            throws Exception {
        Subject subject = getSubject(request, response);
        String[] roles = (String[]) mappedValue;
        if (roles == null || roles.length == 0 ) {
            return true;
        }
        for (String role : roles) {
            if (subject.hasRole(role)) {
                return true;
            }
        }
        return false;
    }
    
}
 

spring.xml:

注入自定义bean

<!-- 自定义file -->
    <bean class="com.fsp.filter.RoleOrFilter" id="roleOrFilter"></bean>

 

<!-- 安全认证过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="login.html" />
        <property name="unauthorizedUrl" value="403.html"/>
       <!-- 自定义授权过滤器-->
        <property name="filters">
        	<map>
        		<entry key="rolesOr" value-ref="roleOrFilter"/>
        	</map>
        </property>
        <property name="filterChainDefinitions">
            <ref bean="shiroFilterChainDefinitions"/>
        </property>
    </bean>

 

<!-- Shiro权限过滤过滤器定义 -->
    <bean name="shiroFilterChainDefinitions" class="java.lang.String">
        <constructor-arg>
            <value>
                /login.html = anon
                /subLogin = anon
                /testRole = roles["admin","admin1"]  //roles必须包含里面两个角色才能执行
                /testRole1 = rolesOr["admin","admin1"] //rolesOr包含其中一个角色就可以执行
                /* = authc
            </value>
        </constructor-arg>
    </bean>

controller:

 

/**roles["admin","admin1"]  //roles必须包含里面两个角色才能执行
	 * @return
	 */
	@RequestMapping(value="/testRole",method=RequestMethod.GET)
	@ResponseBody
	public String testRole() {
		return "testRole success";
	}
	
	
	/**rolesOr["admin","admin1"] //rolesOr包含其中一个角色就可以执行
	 * @return
	 */
	@RequestMapping(value="/testRole1",method=RequestMethod.GET)
	@ResponseBody
	public String testRole1() {
		return "testRole success";
	}

shiro会话管理

shiro session管理

sessionmanager,sessionDAO

Redis实现session共享

redis实现Session共享存在的问题

引入maven依赖:

<!-- 通过redis实现Session共享 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.8.0</version>
        </dependency>

spring.xml配置文件添加:

<!-- 创建sessionManager对象 -->
    <bean class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager" id="sessionManager">
        <property name="sessionDAO" ref="redisSessionDao"/>
    </bean>
    
    <bean class="com.fsp.session.RedisSessionDao" id="redisSessionDao"/>
    

<!--修改-->

 <!--   定义Shiro安全管理配置 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="realm" />
        <property name="sessionManager" ref="sessionManager"/>
    </bean>

RedisSessionDao:

package com.fsp.session;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.SerializationUtils;

import com.fsp.util.JedisUtil;

/**
 * @author fusiping
 * 2018年11月27日
 */
public class RedisSessionDao extends AbstractSessionDAO{
    
    @Autowired
    private JedisUtil jedisUtil;
    
    //前缀
    private final String SHIRO_SESSION_PREFIX = "fsp-session";
    
    //前缀+key组成的key
    private byte[] getKey(String key) {
        return (SHIRO_SESSION_PREFIX + key).getBytes();
    }
    
    /**保存session
     * @param session
     */
    private void savaSession(Session session) {
        if (session != null && session.getId() != null) {
            byte[] key = getKey(session.getId().toString());
            byte[] value = SerializationUtils.serialize(session);
            jedisUtil.set(key,value);
            jedisUtil.expire(key,600);
        }
    }
    /* 创建sessionID
     * @param session
     * @return
     */
    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = generateSessionId(session);
        //捆绑session,sessionId
        assignSessionId(session, sessionId);
        savaSession(session);
        return sessionId;
    }
    
    @Override
    protected Session doReadSession(Serializable sessionId) {
        System.out.println("read session");
        if (sessionId == null) {
            return null;
        }
        byte[] key = getKey(sessionId.toString());
        byte[] value = jedisUtil.get(key);
        //反序列化成json对象
        return (Session) SerializationUtils.deserialize(value);
    }

    /* 更新session
     * @param session
     * @throws UnknownSessionException
     */
    public void update(Session session) throws UnknownSessionException {
        savaSession(session);
    }

    /* 删除session
     * @param session
     */
    public void delete(Session session) {
        if (session == null || session.getId() == null) {
            return ;
        }
        byte[] key = getKey(session.getId().toString());
        jedisUtil.del(key);
    }

    /* 获取正在活动的session
     * @return
     */
    public Collection<Session> getActiveSessions() {
        Set<byte[]> keys = jedisUtil.keys(SHIRO_SESSION_PREFIX);
        Set<Session> sessions = new HashSet<Session>();
        if (CollectionUtils.isEmpty(keys)) {
            return sessions;
        }
        for (byte[] key : keys) {
            Session session = (Session) SerializationUtils.deserialize(jedisUtil.get(key));
            sessions.add(session);
        }
        return sessions;
    }

    

    

}
 

添加spring-redis.xml:

<?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-4.0.xsd">
    
   <bean class="redis.clients.jedis.JedisPool" id="jedisPool">
           <constructor-arg ref="jedisPoolConfig"/>
           <constructor-arg value="127.0.0.1"/>
           <constructor-arg value="6379"/>
   </bean>
   
   <bean class="redis.clients.jedis.JedisPoolConfig" id="jedisPoolConfig"/>
</beans> 

spring.xml引入spring-resdis.xml:

 <import resource="spring-redis.xml"/>

 JedisUtil工具类:

package com.fsp.util;

import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import lombok.val;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/**redis访问工具包
 * @author fusiping
 * 2018年11月27日
 */
@Component
public class JedisUtil {

    @Autowired
    private JedisPool jedisPool;
 
    private Jedis getResource() {
        return jedisPool.getResource();
    }
    
    public byte[] set(byte[] key, byte[] value) {
        Jedis jedis = getResource();
        try {
            jedis.set(key, value);
            return value;
        } finally {
            jedis.close();
        }
    }

    public void expire(byte[] key, int i) {
        Jedis jedis = getResource();
        try {
            jedis.expire(key, i);
        } finally {
            jedis.close();
        }
    }

    public byte[] get(byte[] key) {
        Jedis jedis = getResource();
        try {
            return jedis.get(key);
        } finally {
            jedis.close();
        }
    }

    public void del(byte[] key) {
        Jedis jedis = getResource();
        try {
            jedis.del(key);
        } finally {
            jedis.close();
        }
    }

    public Set<byte[]> keys(String sHIRO_SESSION_PREFIX) {
        Jedis jedis = getResource();
        try {
            //转成二进制
            return jedis.keys((sHIRO_SESSION_PREFIX + "*").getBytes());
        } finally {
            jedis.close();
        }
    }
    
    
    
}
 

结果:

 

注意:必须先启动redis:运行命令:

 

但是每次请求都会调用多次session,需要优化性能:

所以自定义 SessionManager:

package com.fsp.session;

import java.io.Serializable;

import javax.servlet.ServletRequest;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;

/**自定义sessionmanager
 * @author fusiping
 * 2018年11月27日
 */
public class CustomSessionManager extends DefaultWebSessionManager {
    
    /* 重新retrieveSession
     * 先从request中获取,取不到则从redis,取到后在set到request
     * @param sessionKey
     * @return
     * @throws UnknownSessionException
     */
    protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
        // 1先获取sessionId
        Serializable sessionId = getSessionId(sessionKey);
        ServletRequest request = null;
        //先反对是不是WebSessionKey类型
        if (sessionKey instanceof WebSessionKey) {
            //从sessionkey总获取request
            request = ((WebSessionKey) sessionKey).getServletRequest();
        }
        //如果request != null && sessionId != null
        if (request != null && sessionId != null) {
            //直接从request中获取session
            return (Session) request.getAttribute(sessionId.toString());
        }
        //那么从retrieveSession中获取
        Session session = super.retrieveSession(sessionKey);
        if (request != null && sessionId != null) {
            request.setAttribute(sessionId.toString(), session);
        }
        return session;
    }

}
 

修改spring.xml:

<!-- 修改sessionManager为自定义对象 -->
    <bean class="com.fsp.session.CustomSessionManager" id="sessionManager">
        <property name="sessionDAO" ref="redisSessionDao"/>
    </bean> 

<bean class="com.fsp.session.RedisSessionDao" id="redisSessionDao"/>

 优化sessionManager

package com.fsp.session;

import java.io.Serializable;

import javax.servlet.ServletRequest;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;

/**自定义sessionmanager
 * @author fusiping
 * 2018年11月27日
 */
public class CustomSessionManager extends DefaultWebSessionManager {
    
    /* 重新retrieveSession
     * 先从request中获取,取不到则从redis,取到后在set到request
     * @param sessionKey
     * @return
     * @throws UnknownSessionException
     */
    @Override
    protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
        // 1先获取sessionId
        Serializable sessionId = getSessionId(sessionKey);
        ServletRequest request = null;
        //先反对是不是WebSessionKey类型
        if (sessionKey instanceof WebSessionKey) {
            //从sessionkey总获取request
            request = ((WebSessionKey) sessionKey).getServletRequest();
        }
        //如果request != null && sessionId != null
        if (request != null && sessionId != null) {
            //直接从request中获取session
            Session session = (Session) request.getAttribute(sessionId.toString());
            if (session != null ) {
                return session;
            }
        }
        //那么从retrieveSession中获取
        Session session = super.retrieveSession(sessionKey);
        if (request != null && sessionId != null) {
            request.setAttribute(sessionId.toString(), session);
        }
        return session;
    }

}
 

 优化之后就不用每次都多次调用redis

spring.xml添加:

 <!--   修改定义Shiro安全管理配置 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="realm" />
        <property name="sessionManager" ref="sessionManager"/>
        <property name="cacheManager" ref="redisCacheManager"/>  //shrio session 缓存
    </bean>

 

<!-- shrio session 缓存 -->
    <bean id="redisCacheManager" class="com.fsp.cache.RedisCacheManager"/>

 

RedisCacheManager:

 

package com.fsp.cache;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;

public class RedisCacheManager implements CacheManager{
    
    @Autowired
    private RedisCache redisCache;

    public <K, V> Cache<K, V> getCache(String arg0) throws CacheException {
        
        return redisCache;
    }

}
 

RedisCache:

package com.fsp.cache;

import java.util.Collection;
import java.util.Set;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.SerializationUtils;

import com.fsp.util.JedisUtil;
@Component
public class RedisCache<K, V> implements Cache<K, V>{
    
    @Autowired
    private JedisUtil jedisUtil;
    
    private final String cache_prefix = "fsp-cache"; 

    private byte[] getKey(K k) {
        if (k instanceof String) {
            return (cache_prefix + k).getBytes();
        }
        return SerializationUtils.serialize(k);
    }
    public void clear() throws CacheException {
        // TODO Auto-generated method stub
        
    }

    public V get(K k) throws CacheException {
        System.out.println("从reids中获取权限数据");
        byte[] value = jedisUtil.get(getKey(k));
        if (value != null ) {
            return (V) SerializationUtils.deserialize(value);
        }
        return null;
    }

    public Set<K> keys() {
        
        return null;
    }

    public V put(K arg0, V arg1) throws CacheException {
        byte[] key = getKey(arg0);
        byte[] value = SerializationUtils.serialize(arg1);
        jedisUtil.set(key, value);
        jedisUtil.expire(key, 600);
        return arg1;
    }

    /* 删除
     * @param arg0
     * @return
     * @throws CacheException
     */
    public V remove(K arg0) throws CacheException {
        byte[] key = getKey(arg0);
        byte[] value = jedisUtil.get(key);
        jedisUtil.del(key);
        if (value != null) {
            return (V) SerializationUtils.deserialize(value);
        }
        return null;
    }

    public int size() {
        // TODO Auto-generated method stub
        return 0;
    }

    public Collection<V> values() {
        // TODO Auto-generated method stub
        return null;
    }

}
 

 记住我功能:

spring.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.0.xsd"
    default-lazy-init="true">

    <import resource="spring-dao.xml"/>
    <import resource="spring-redis.xml"/>
    <context:component-scan base-package="com.fsp"/>
    
    <!-- 安全认证过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="login.html" />
        <property name="unauthorizedUrl" value="403.html"/>
        <!-- 
        <property name="filters">
            <map>
                <entry key="cas" value-ref="casFilter"/>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property> -->
        <property name="filters">
            <map>
                <entry key="rolesOr" value-ref="roleOrFilter"/>
            </map>
        </property>
        <property name="filterChainDefinitions">
            <ref bean="shiroFilterChainDefinitions"/>
        </property>
    </bean>
    
    <!-- Shiro权限过滤过滤器定义 -->
    <bean name="shiroFilterChainDefinitions" class="java.lang.String">
        <constructor-arg>
            <value>
                /login.html = anon
                /subLogin = anon
                /testRole = roles["admin","admin1"]
                /testRole1 = rolesOr["admin","admin1"]
                <!-- /testRole = roles["admin"]
                /testRole1 = roles["admin","admin1"] 同时具备admin,admin1
                /testperms = perms["user:delete"]
                /testperms1 = perms["user:delete","user:update"] 同时具备user:delete,user:update -->
                /* = authc
            </value>
        </constructor-arg>
    </bean>
    
    <!-- 自定义file -->
    <bean class="com.fsp.filter.RoleOrFilter" id="roleOrFilter"></bean>
    
    <!--   定义Shiro安全管理配置 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="realm" />
        <property name="sessionManager" ref="sessionManager"/>
        <property name="cacheManager" ref="redisCacheManager"/>
        <property name="rememberMeManager" ref="cookieRememberMeManager"/>
    </bean>
    
    <bean class="com.fsp.shiro.realm.CustomRealm" id="realm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>
    
    <!-- 加密对象-->
    <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" id="credentialsMatcher">
        <property name="hashAlgorithmName" value="MD5"/>
        <property name="hashIterations" value="1"/>
    </bean>

    <!-- 创建sessionManager对象 -->
    <bean class="com.fsp.session.CustomSessionManager" id="sessionManager">
        <property name="sessionDAO" ref="redisSessionDao"/>
    </bean>
    
    <bean class="com.fsp.session.RedisSessionDao" id="redisSessionDao"/>
    
    <!-- shrio session 缓存 -->
    <bean id="redisCacheManager" class="com.fsp.cache.RedisCacheManager"/>
    
    <bean class="org.apache.shiro.web.mgt.CookieRememberMeManager" id="cookieRememberMeManager">
        <property name="cookie" ref="simpleCookie"/>
    </bean>
    
    <bean class="org.apache.shiro.web.servlet.SimpleCookie" id="simpleCookie">
        <constructor-arg value="rememberMe"/>
        <property name="maxAge" value="20000000"/>
    </bean>
    
</beans>

loginController:

@RequestMapping(value="/subLogin",method = RequestMethod.POST,produces = "application/json;charset=utf-8")
	@ResponseBody
	public String subLogin(UserModel userModel) {
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken(userModel.getUsername(),userModel.getPassword());
		try {
			token.setRememberMe(userModel.isRememberMe());
			subject.login(token);
		} catch (AuthenticationException e) {
			return "登陆异常:"+e.getMessage();
		}
		
		if (subject.hasRole("admin")) {
			subject.checkPermission("user:delete");
			return "有admin权限";
			
		}
		return "无admin权限";
	}

login.xml:

 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="subLogin" method="post">
    用户名:<input type="text" name="username"/><br>
    密码:<input type="password" name="password"/><br>
    <input type="checkbox" name="rememberMe"/>记住我<br>
    <input type="submit" value="登陆">
</form>

</body>
</html>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员雪球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值