SSM整合shiro进行权限控制以及shiro的一些特殊功能实现

项目结构图:

SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第1张图片
强力推荐《跟我学shiro》!!

一、先新建一个maven项目,配置pom.xml


  4.0.0
  com.test
  Test
  war
  0.0.1
  Test
  http://maven.apache.org
  
		1.8
		1.8
	
  
    
      junit
      junit
      3.8.1
      test
    
    
		
			org.springframework
			spring-aop
			4.3.2.RELEASE
		
		
		
			org.aspectj
			aspectjweaver
			1.6.11
			jar
			compile
		
		
			cglib
			cglib
			3.2.4
		
		
			org.springframework
			spring-beans
			4.3.2.RELEASE
		
		
			org.springframework
			spring-webmvc
			4.3.2.RELEASE
		
		
			org.springframework
			spring-jdbc
			4.3.2.RELEASE
		
		
			org.springframework
			spring-web
			4.3.2.RELEASE
		
		
		
			org.springframework
			spring-tx
			4.3.2.RELEASE
		
		
		
		
		
			commons-dbcp
			commons-dbcp
			1.4
		
		
		
			mysql
			mysql-connector-java
			
			5.1.18
		
		
		
			org.mybatis
			mybatis
			3.4.2
		
		
		
			org.mybatis
			mybatis-spring
			1.3.0
		
		
		
			org.mybatis
			mybatis-ehcache
			1.0.0
		
		
		
		
		
			log4j
			log4j
			1.2.17
		
		
		
			com.alibaba
			fastjson
			1.1.41
		
		
		
			org.slf4j
			slf4j-api
			1.7.7
		
		
			org.slf4j
			slf4j-log4j12
			1.7.7
		
		
		
		
		
			org.codehaus.jackson
			jackson-mapper-asl
			1.9.13
		
		
		
		
			commons-fileupload
			commons-fileupload
			1.3.1
		
		
		
			commons-io
			commons-io
			2.4
		
		
		
			commons-codec
			commons-codec
			1.9
		
		
		
			javax.servlet
			javax.servlet-api
			3.1.0
		
		
		
			org.apache.shiro
			shiro-core
			1.3.2
		
		
			org.apache.shiro
			shiro-web
			1.3.2
		
		
			org.apache.shiro
			shiro-ehcache
			1.3.2
		
		
			org.apache.shiro
			shiro-spring
			1.3.2
		
		
		
			com.google.collections
			google-collections
			1.0
		
		
		
			com.google.guava
			guava
			19.0
		
		
		
			org.projectlombok
			lombok
			1.16.16
		
		
		
			javax.servlet.jsp
			jsp-api
			2.2
		
		
		
			jstl
			jstl
			1.2
		
		
		
		
			com.fasterxml.jackson.core
			jackson-core
			2.8.3
		
		
		
			org.codehaus.jackson
			jackson-core-asl
			1.9.13
		
		
		
			com.fasterxml.jackson.core
			jackson-databind
			2.8.3
		
		
		
			com.fasterxml.jackson.core
			jackson-annotations
			2.8.3
		

		
			org.apache.shiro
			shiro-quartz
			1.2.2
		
		
		
			org.springframework
			spring-context-support
			3.2.8.RELEASE
		
		
			net.sf.ehcache
			ehcache-core
			2.6.11
		
		
   		commons-collections
      		commons-collections
      		3.2.1
  		
  		    
            org.springframework    
            spring-test    
             3.2.4.RELEASE      
            provided    
        
  
  
    Test
    
			
			
				
					org.mybatis.generator
					mybatis-generator-maven-plugin
					1.3.2
					
						src/main/resources/generator.xml
						true
						true
					
					
						
							Generate MyBatis Artifacts
							
								generate
							
						
					
					
						
							org.mybatis.generator
							mybatis-generator-core
							1.3.2
						
					
				
				
				
					org.apache.maven.plugins
					maven-surefire-plugin
					2.19.1
					
						true
					
				
				
				
					org.apache.maven.plugins
					maven-resources-plugin
					3.0.1
					
						UTF-8
					
				
				
				
					org.apache.tomcat.maven
					tomcat7-maven-plugin
					2.2
				
			
		
		
		
			
				src/main/resources
				
					**/*
				
				true
			
		
  
 
  
		
			dev
			
				true
			
			
				
				192.168.*.*:3306/dev_test
			
			
			
				
					src/main/resources/jdbc.properties
					src/main/resources/log4j.properties
				
			
		

		
			test
			
				
				192.168.*.*:3306/creditloan_ph
			
			
				
					src/main/resources/jdbc.properties
					src/main/resources/log4j.properties
				
			
		
		
			product
			
				
				192.168.*.*:3306/creditloan_ph
			
			
				
					src/main/resources/jdbc.properties
					src/main/resources/log4j.properties
				
			
		

	


附:generator.xml

 

	
	  
	
		
			
			
		
		
		
			
		
		
			
			
		
		
		
			
			
			
			
		
		
		
			
			
		
		
		
			
			
		
		
		

二、先把ssm的基本配置贴上来,就不详细赘述了:

1、application-context.xml(spring管理的相关配置)




	  
	
	
	
	
	
		
			
				classpath:jdbc.properties

			
		
	
	
	
	
		
		
		
		
		
		
		
		
		
		
		
		
		
		
	
	
	
		
		
		
		
			
				classpath:com/loan/dao/xml/*.xml
			
		
	
	
	
		
		
	
	
	
	
		
	
	
	
	
		
			
			
			
			
			
			
			
			
			
		
	
	
		
		
	
      
      

附:

a、jdbc.properties
connection.driverClassName=com.mysql.jdbc.Driver
connection.url=jdbc:mysql://192.168.*.*:3306/dev_test?useUnicode=true&characterEncoding=UTF-8
connection.username=user
connection.password=pwd

connection.initialSize=0
connection.maxActive=100
connection.maxIdle=30
connection.minIdle=5 
connection.maxWait=5000
connection.removeAbandoned=true
connection.removeAbandonedTimeout=3000
connection.logAbandoned=false
connection.defaultAutoCommit=true
connection.defaultReadOnly=false
connection.validationQuery=SELECT 1
connection.testOnBorrow=true
b、mybatis-config.xml



    
    
        
        
        
        
        
        
        
        
        
        
        
        
        
        

        
        
    




2、spring-mvc.xml(SpringMVC的相关配置)



	
	
	
	
		
		
	



4、log4j.properties

log4j.rootLogger=INFO, stdout, logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.threshold=INFO
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d \u4FE1\u9500\u7F51\u7AD9\u540E\u53F0\u7BA1\u7406 -->%5p{%F:%L}-%m%n

log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.threshold=ERROR
log4j.appender.logfile.File=${catalina.home}/logs/Test/Test
log4j.appender.logfile.DatePattern='-'yyyy-MM-dd-HH-mm'.log'
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d \u4FE1\u9500\u7F51\u7AD9\ -->%5p{%F:%L}-%m%n


5、web.xml



	
		webAppRootKey
		test
	
	
		log4jConfigLocation
		classpath:log4j.properties
	
	
		org.springframework.web.util.Log4jConfigListener
	
	
		CharacterEncodingFilter
		org.springframework.web.filter.CharacterEncodingFilter
		
			encoding
			UTF-8
		
		
			forceEncoding
			true
		
	
	
		CharacterEncodingFilter
		/*
	
	
		contextConfigLocation
		
             classpath:application-context.xml
             classpath:spring-shiro.xml
         
	
	
		org.springframework.web.context.ContextLoaderListener
	
	
		dispatcher
		org.springframework.web.servlet.DispatcherServlet
		
			contextConfigLocation
			classpath:spring-mvc.xml
		
		1
	
	
		dispatcher
		/
	
	
		30
	



至此,ssm基本配置完成

三、整合shiro

1、spring-shiro.xml


	
	
		
	
	
	
		
		
		
		
	
	
	
		
		
		
		
		
		
		
	
	
	
	
	
		
		
	
	
	
		
		
	
	
	
		
		
		
	
	
	
		
		
		
	
	
	
		
		
	
	
	
		
		
		
		
		
		
		
		
	
	
	
	
	
		
		
		
		
	
	
	
		
		
		
		
		
	
	
	
		
		
	
	
	
	
		
		
		
		
	
	
	
		
		
		
		
		
			
				
				
			
		
		
			
				/static/** = anon
				/index = anon
				/unauthorized = anon
				/login =authc
				/logout = logout
				/admin/**=user,kickout
			
		
	
	  
      
          
              
                /unauthorized  
              
              
                /login
              
          
      
 



附:

a、ehcache.xml




    

    
    
    
    
    
	
    
    
	
    
    
	
    




b、RetryLimitHashedCredentialsMatcher.java
package com.loan.credentials;

import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.Resource;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.SaltedAuthenticationInfo;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;


/**
 * 

* User: Zhang Kaitao *

* Date: 14-1-28 *

* Version: 1.0 */ public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher { //AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减。 private Cache passwordRetryCache; public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) { passwordRetryCache = cacheManager.getCache("passwordRetryCache"); } //控制密码输入错误次数 @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { String username = (String) token.getPrincipal(); // retry count + 1 AtomicInteger retryCount = passwordRetryCache.get(username); System.out.println("retryCount:"+retryCount); if (retryCount == null) { retryCount = new AtomicInteger(0); passwordRetryCache.put(username, retryCount); } if (retryCount.incrementAndGet() > 5) { // if retry count > 5 throw throw new ExcessiveAttemptsException(); } boolean matches = super.doCredentialsMatch(token, info); if (matches) { // clear retry count passwordRetryCache.remove(username); } return matches; } }


c、UserRealm.java

package com.loan.realm;

import org.apache.log4j.Logger;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.loan.entity.User;
import com.loan.pojo.UserParams;
import com.loan.service.ResourceService;
import com.loan.service.RoleService;
import com.loan.service.UserRoleService;
import com.loan.service.UserService;


/** 
* @ClassName: UserRealm 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author jiayq
* @date 2016年9月5日 上午10:09:51 
*  
*/
public class UserRealm extends AuthorizingRealm {
	private static final Logger logger = Logger.getLogger(UserRealm.class);

	@javax.annotation.Resource
	private UserRoleService userRoleService;
	@javax.annotation.Resource
	private ResourceService resourceService;
	@javax.annotation.Resource
	private UserService userService;
	@javax.annotation.Resource
	private RoleService roleService;
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		//直接调用getPrimaryPrincipal得到之前传入的用户名
		User user = (User) principals.getPrimaryPrincipal();
		logger.info("[用户:" + user.getUsername() + "|权限授权]");
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		//根据用户名调用UserService接口获取角色及权限信息
		authorizationInfo.setRoles(roleService
				.loadRoleIdByUsername(user.getUsername()));
		authorizationInfo.setStringPermissions(resourceService
				.loadPermissionsByUsername(user.getUsername()));
		logger.info("[用户:" + user.getUsername() + "|权限授权完成]");
		return authorizationInfo;
	}

	@Override
	public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
		// 获取基于用户名和密码的令牌
		// 实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的
		UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
		String username = (String) token.getPrincipal();
		System.out.println("pwd:"+token.getCredentials().toString());
		logger.info("[用户:" + username + "|系统权限认证]");
		User u = new User();
		u.setUsername(username);
		if (userService.find(new UserParams(u)).size() > 0) {
			User sqluser = userService.find(new UserParams(u)).get(0);
			// 交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现
			System.out.println("Realm:"+ByteSource.Util.bytes(sqluser.getCredentialsSalt()));
			SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(sqluser, sqluser.getPassword(),
					ByteSource.Util.bytes(sqluser.getCredentialsSalt()), this.getName());// realm
			logger.info("[用户:" + username + "|系统权限认证完成]");
			return authenticationInfo;
		}
		return null;
	}
}

d、MySessionListener1(用户测试会话过期时间)

package com.loan.util;
import java.util.Date;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;

public class MySessionListener1 implements SessionListener {
@Override
public void onStart(Session session) {//会话创建时触发
System.out.println("会话创建:" + session.getId()+"》》时间:"+session.getLastAccessTime());
}
@Override
public void onExpiration(Session session) {//会话过期时触发
System.out.println("会话过期:" + session.getId()+"》》时间:"+new Date());
}
@Override
public void onStop(Session session) {//退出/会话过期时触发
System.out.println("会话停止:" + session.getId()+"》》时间:"+session.getLastAccessTime());
}
}

e、KickoutSessionControlFilter.java

package com.loan.util;

import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;

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

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;

import com.loan.entity.User;

public class KickoutSessionControlFilter  extends AccessControlFilter{
	private String kickoutUrl; //踢出后到的地址
    private boolean kickoutAfter; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
    private int maxSession; //同一个帐号最大会话数 默认1
    private SessionManager sessionManager;
    private Cache> cache;

    public void setKickoutUrl(String kickoutUrl) {
        this.kickoutUrl = kickoutUrl;
    }

    public void setKickoutAfter(boolean kickoutAfter) {
        this.kickoutAfter = kickoutAfter;
    }

    public void setMaxSession(int maxSession) {
        this.maxSession = maxSession;
    }

    public void setSessionManager(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    public void setCacheManager(CacheManager cacheManager) {
        this.cache = cacheManager.getCache("shiro-activeSessionCache");
    }
     /**
      * 是否允许访问,返回true表示允许
      */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    	return false;
    }
    /**
     * 表示访问拒绝时是否自己处理,如果返回true表示自己不处理且继续拦截器链执行,返回false表示自己已经处理了(比如重定向到另一个页面)。
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        Subject subject = getSubject(request, response);
        if(!subject.isAuthenticated() && !subject.isRemembered()) {
            //如果没有登录,直接进行之后的流程
            return true;
        }

        Session session = subject.getSession();
        String username = ((User)(subject.getPrincipal())).getUsername();
        Serializable sessionId = session.getId();

        // 初始化用户的队列放到缓存里
        Deque deque = cache.get(username);
        if(deque == null) {
            deque = new LinkedList();
            cache.put(username, deque);
        }

        //如果队列里没有此sessionId,且用户没有被踢出;放入队列
        if(!deque.contains(sessionId) && session.getAttribute("kickout") == null) {
            deque.push(sessionId);
        }
        //如果队列里的sessionId数超出最大会话数,开始踢人
        while(deque.size() > maxSession) {
            Serializable kickoutSessionId = null;
            if(kickoutAfter) { //如果踢出后者
            	kickoutSessionId=deque.getFirst();
                kickoutSessionId = deque.removeFirst();
            } else { //否则踢出前者
                kickoutSessionId = deque.removeLast();
            }
            try {
            	Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
                if(kickoutSession != null) {
                    //设置会话的kickout属性表示踢出了
                    kickoutSession.setAttribute("kickout", true);
                }
            } catch (Exception e) {//ignore exception
            	e.printStackTrace();
            }
        }

        //如果被踢出了,直接退出,重定向到踢出后的地址
        if (session.getAttribute("kickout") != null) {
            //会话被踢出了
            try {
                subject.logout();
            } catch (Exception e) { 
            }
            WebUtils.issueRedirect(request, response, kickoutUrl);
            return false;
        }
        return true;
    }
}

2、spring-mvc.xml中加入shiro注解配置


	
	
	
		
	
	 

3、web.xml中加入过滤器shiroFilter


		shiroFilter
		org.springframework.web.filter.DelegatingFilterProxy
		
			targetFilterLifecycle
			true
		
	
	
		shiroFilter
		/*
	



四、基本配置已经完成,接下来通过简单的demo对逻辑进行梳理

1、登录验证逻辑

基本思路:新建用户(密码加密)->利用shiro的credentialsMatcher进行验证->完成登录( 重点:密码加密方式与credentialsMatcher的验证方式一致,当然你也可以自己实现验证)

(1)建立sys_user数据库表


SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第2张图片

(2)实体类User.java

package com.loan.entity;

import java.io.Serializable;
import java.util.List;

import lombok.Data;
/** 
* @ClassName: User 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author jiayq
* @date 2016年9月5日 下午2:31:58 
*  
*/
@SuppressWarnings("serial")
@Data
public class User implements Serializable {
	private Long id;
	private String username;// 用户名
	private String workNo;// 工作编号
	private String salt;// 盐(密码安全)
	private String password;// 密码
	private Integer age;// 年龄
	private String state;// 状态
	private Long orgId;
	private String pic;
	private String phone;
	private String address;
	private String email;
	private Integer percent;
	/**
	 * @Title: getCredentialsSalt
	 * @Description: salt = salt + username
	 * @param @return 设定文件
	 * @return String 返回类型
	 * @author jiayq
	 * @throws
	 */
	public String getCredentialsSalt() {
		return username + salt;
	}
	
}

(3)UserMapper.xml





	
	
	insert into sys_user(id,username,work_no,salt,password,age,state,org_id,pic,phone,address,email,percent) values (#{id},#{username},#{workNo},#{salt},#{password},#{age},#{state},#{orgId},#{pic},#{phone},#{address},#{email},#{percent})
	

(4)UserMapper.java

package com.loan.dao.mapper;

import java.util.List;

import com.loan.entity.User;
import com.loan.pojo.UserParams;


/** 
* @ClassName: UserMapper 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author jiayq
* @date 2016年9月29日 下午4:08:43 
*  
*/
public interface UserMapper {
	public void save(User user);
}

(5)UserService.java

package com.loan.service;

import java.util.List;

import com.loan.entity.User;

/** 
* @ClassName: UserService 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author jiayq
* @date 2016年9月7日 上午10:09:32 
*  
*/
public interface UserService {

	public void save(User user);

}

(6)UserServiceImpl.java

package com.loan.service.impl;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.loan.dao.mapper.UserMapper;
import com.loan.dao.mapper.UserRoleMapper;
import com.loan.entity.User;
import com.loan.pojo.UserParams;
import com.loan.service.UserService;
import com.loan.util.EndecryptUtils;
import com.loan.util.PasswordHelper;
/** 
* @ClassName: UserServiceImpl 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author jiayq
* @date 2016年9月6日 上午9:24:49 
*  
*/
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Resource 
private UserRoleMapper userRoleMapper;
@Override
public void save(User user) {
	User u=new PasswordHelper().encryptPassword(user);
	userMapper.save(u);
	
}

}

(7)PasswordHelper.java

package com.loan.util;

import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;

import com.loan.entity.User;

public class PasswordHelper {
	private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
	private String algorithmName = "md5";
	private final int hashIterations = 2;

	public User encryptPassword(User user) {
		if(user.getSalt()==null||("").equals(user.getSalt())){
			user.setSalt(randomNumberGenerator.nextBytes().toHex());
		}
		String newPassword = new SimpleHash(algorithmName, user.getPassword(),
				ByteSource.Util.bytes(user.getCredentialsSalt()), hashIterations).toHex();
		user.setPassword(newPassword);
		return user;
	}
}

重点:
SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第3张图片

(8)新建用户

package com.loan.controller;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.stereotype.Controller;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.loan.entity.User;
import com.loan.service.UserService;

@Controller
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/application-context.xml")
@RequestMapping("/test")
public class TestController {
@Resource
private UserService userService;
@Test
public void userCreate(){
	User user=new User();
	user.setAge(20);
	user.setPassword("123");
	user.setUsername("shiroUser");
	userService.save(user);
}
}

右键run as JUnit test

(9)简单login.jsp页面进行登录

<%@ page contentType="text/html;charset=UTF-8" language="java"%>


 
   
   
   
  登录 
   
   
  
  
  
        

(10)LoginController.java

package com.loan.controller;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
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.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.loan.entity.User;
import com.loan.service.UserService;
/** 
* @ClassName: LoginController 
* @Description: TODO(登录controller) 
* @author jiayq
* @date 2016年9月5日 下午5:06:33 
*  
*/
@Controller
@RequestMapping("/")
public class LoginController {
	@Resource
	private UserService userService;
	/** 
	* @Title: loginView 
	* @Description: TODO(转向登录界面) 
	* @param @return    设定文件 
	* @return String    返回类型 
	* @throws 
	*/
	@RequestMapping(value = "/login")
	public String loginView(){
		return "login";
	}

	/** 
	* @Title: login1 
	* @Description: TODO(shiro+EndecryptUtils进行认证) 
	* @param @param request
	* @param @param model
	* @param @param username
	* @param @param password
	* @param @return    设定文件 
	* @return String    返回类型 
	* @throws 
	*/
	@RequestMapping(value = "/loginTest")
	public String login(HttpServletRequest request, Model model, String username, String password,boolean rememberMe) {
		System.out.println("rememberMe:"+rememberMe);
		//将form中的用户名密码传入Realm 的doGetAuthenticationInfo
		UsernamePasswordToken token = new UsernamePasswordToken(username, password.toCharArray());
		token.setRememberMe(rememberMe);
		Subject currentUser = SecurityUtils.getSubject();
		String error = "";
		try {
			currentUser.login(token);
		} catch (UnknownAccountException ex) {// 用户名没有找到
			error = "您输入的用户名不存在!";
		} catch (IncorrectCredentialsException ex) {// 用户名密码不匹配
			error = "用户名密码不匹配 !";
		}
		catch(ExcessiveAttemptsException e){
			error="密码错误次数已超五次,账号锁定1小时!";
		}
		catch (AuthenticationException ex) {// 其他的登录错误
			ex.printStackTrace();
			error = "其他的登录错误  !";
		}
		// 验证是否成功登录的方法
		if (currentUser.isAuthenticated()) {
			return "redirect:/admin/index";
		} else {
			model.addAttribute("message", error);
			currentUser.logout();
			return "login";
		}

	}
	
	
}


结果:

SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第4张图片

重点逻辑:

SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第5张图片

五、密码输入错误账号锁定功能

前面给的代码中已经实现,主要梳理下逻辑:
SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第6张图片

六、并发登录人数控制

代码上头已经给出,主要梳理流程:

SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第7张图片

七、记住密码

spring-shiro.xml中相关配置:

SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第8张图片

jsp:


SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第9张图片

LoginController.java

SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第10张图片

八、shiro的权限授权

1、建立用户、角色、资源以及之间的相关表
(1)sys_user:
SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第11张图片
User.java
package com.loan.entity;

import java.io.Serializable;
import java.util.List;

import lombok.Data;
/** 
* @ClassName: User 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author jiayq
* @date 2016年9月5日 下午2:31:58 
*  
*/
@SuppressWarnings("serial")
@Data
public class User implements Serializable {
	private Long id;
	private String username;// 用户名
	private String workNo;// 工作编号
	private String salt;// 盐(密码安全)
	private String password;// 密码
	private Integer age;// 年龄
	private String state;// 状态
	private Long orgId;
	private String pic;
	private String phone;
	private String address;
	private String email;
	private Integer percent;
	/**
	 * @Title: getCredentialsSalt
	 * @Description: salt = salt + username
	 * @param @return 设定文件
	 * @return String 返回类型
	 * @author jiayq
	 * @throws
	 */
	public String getCredentialsSalt() {
		return username + salt;
	}
	
}


(2)sys_user_role:
SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第12张图片
UserRole.java
package com.loan.entity;

import java.io.Serializable;

import lombok.Data;
/** 
* @ClassName: UserRole 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author jiayq
* @date 2016年9月20日 上午10:17:50 
*  
*/
@SuppressWarnings("serial")
@Data
public class UserRole implements Serializable {
	private Long id;
	private Long roleId;//角色id
	private Long userId;//用户id

}


(3)sys_role:
SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第13张图片
Role.java
package com.loan.entity;

import java.io.Serializable;

import lombok.Data;
/** 
* @ClassName: Role 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author jiayq
* @date 2016年9月20日 上午10:15:29 
*  
*/
@SuppressWarnings("serial")
@Data
public class Role implements Serializable{
	private Long id;
	private String name;//名称
	private String description;//描述
	private String state;//状态
	private String code;//编码
	private Long pid ;//父id
	private String remark;//备注
	//关联资源列表
	private String resources;
	
	
}


(4)sys_resource:
SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第14张图片
Resource.java
package com.loan.entity;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import lombok.Data;
/** 
* @ClassName: Resource 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author jiayq
* @date 2016年9月19日 下午4:57:11 
*  
*/
@SuppressWarnings("serial")
@Data
public class Resource implements Serializable{
	private Long id;
	private String name;//名称
	//private ResourceType type = ResourceType.menu;//资源类型
	private String type;
	private Integer leaf;//0表示是叶子节点
	private Long priority;//顺序
	private Long pid;//父id
	private String permission;//权限
	private String status;//状态
	private String url;//路径
	private String outUrl;//站外路径
	private String pic;
	//关联的角色列表
	private List roles;
	private List children = new ArrayList();
}


(5)sys_role_resource:
SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第15张图片
RoleResource.java
package com.loan.entity;

import java.io.Serializable;

import lombok.Data;

/** 
* @ClassName: RoleResource 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author jiayq
* @date 2016年9月20日 上午10:16:57 
*  
*/
@Data
public class RoleResource implements Serializable{
	private Long id;
	private Long roleId;//角色id
	private Long resourceId;//资源id

}

2、新建角色Role为角色分配资源Resource(权限)

(1)新建资源:

@Test
public void resourceCreate(){
	//建立三个资源
	for(int i=1;i<4;i++){
		com.loan.entity.Resource resource=new com.loan.entity.Resource();
		resource.setName("resourceTest_"+i);
		resource.setPermission("test:permission_"+i);
		resourceService.save(resource);
	}
}

(2)新建role

@Test
public void roleCreate(){
	Role role=new Role();
	role.setName("testRole");
	roleService.save(role);
	for(int i=143;i<146;i++){
		RoleResource roleResource=new RoleResource();
		roleResource.setRoleId(role.getId());
		roleResource.setResourceId((long)i);
		roleResourceService.save(roleResource);	
	}
}

SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第16张图片

(3)新建user

@Test
public void UserCreate(){
	User user=new User();
	user.setAge(20);
	user.setPassword("123");
	user.setUsername("shiroUser1");
	userService.save(user);
	UserRole ur=new UserRole();
	ur.setRoleId((long)25);
	ur.setUserId(user.getId());
	userRoleService.save(ur);
}

SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第17张图片

至此,所以关系逻辑建立。

3、UserRealm权限授权

@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		//直接调用getPrimaryPrincipal得到之前传入的用户名
		User user = (User) principals.getPrimaryPrincipal();
		logger.info("[用户:" + user.getUsername() + "|权限授权]");
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		//根据用户名调用UserService接口获取角色及权限信息
		authorizationInfo.setRoles(roleService
				.loadRoleIdByUsername(user.getUsername()));
		authorizationInfo.setStringPermissions(resourceService
				.loadPermissionsByUsername(user.getUsername()));
		logger.info("[用户:" + user.getUsername() + "|权限授权完成]");
		return authorizationInfo;
	}
roleMapper.xml

resourceMapper.xml





3、通过jsp/注释测试

(1)jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>




test
 




欢迎游客访问,登录


欢迎登录,退出


用户[]已身份验证通过
用户[]拥有testRole角色

用户[]拥有权限test:permission_1

登录结果:

SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第18张图片

(2)通过注解控制方法的访问:

@RequiresPermissions("test:permission_1")
@RequestMapping("/testAnnotation")
public String testAnnotation(){
	return "/index";
}

@RequiresPermissions("test:nopermission")
@RequestMapping("/testAnnotation1")
public String testAnnotation1(){
	return "index";
}


测试结果:

SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第19张图片


SSM整合shiro进行权限控制以及shiro的一些特殊功能实现_第20张图片

终于完了!!!

源代码下载:http://download.csdn.net/download/dreamer_8399/9959021


你可能感兴趣的:(框架)