SSM整合系列之 整合Shiro实现登陆认证

前言:Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理,本文将介绍Spring整合Shiro实现登陆认证功能。对Shiro需要更深入的了解,请自学(一个礼貌的微笑)本人后续也会详细总结Shiro。继本文之后我也将继续完善登陆的RemberMe功能,以及Shiro+Memcache(也可以是其他如Redis)实现分布式Session共享功能
首先简单了解下Shiro的三个核心组件:Subject, SecurityManager 和 Realms。
  Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
  SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
  Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
  从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权
开始之前可以先了解本系列的其他文章,上一篇文章地址:https://blog.csdn.net/caiqing116/article/details/84581171
1.首先我们插入一个管理员账号作为登陆测试数据,执行上一篇文章的测试用例,或者手动插入一条数据皆可

@Test
public void testInsert() {
	BasicUser basicUser = new BasicUser();
	basicUser.setId(1);
	basicUser.setUtype(2);
	basicUser.setUserid(UuidUtil.getUuid());
	basicUser.setUsername("墨倾池");
	basicUser.setRealname("墨倾池");//注意 补充了真实姓名的插入
	basicUser.setPassword(EncryptKit.MD5("123456"));
	basicUser.setAge(18);
	int result = basicUserService.insert(basicUser);
	log.info("basicUser:"+basicUser);
	log.info("插入行数:"+result);
}
或者手动插入:INSERT INTO tb_basic_user(userId,utype,username,password,realname,age)
VALUES("3b006cc66a174d668b5cd0ea83eedd0d",1,"admin","E10ADC3949BA59ABBE56E057F20F883E","管理员",18);

2.Maven引入shiro需要的jar包

	
		org.apache.shiro
		shiro-core
		1.2.3
	
	
		org.apache.shiro
		shiro-web
		1.2.3
	
	
		org.apache.shiro
		shiro-spring
		1.2.3
		

3.配置Spring shiro整合文件 /spring/applicationContext-shiro.xml,具体功能详见注释



	
	Spring Shiro整合配置文件

    
    
 	
        
    
    
    
      
	
	
    
    
    
    
    
    
    
         
    
          
    
    
    
    
        
        
        
        
        
        
        
      	
        
            
            	
            	/static/**= anon
            	/ssm/shirologin/** = anon
            	/ssm/logout = logout
                
                /** = authc
            
        
    

  	
    
        
    
    
    
    
        
            
            	
                /ssm/home
            
        
    

4.实现Shiro Realm
创建包security创建类ShiroRealm 继承 AuthorizingRealm,具体实现如下

package com.ssm.security;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import com.ssm.entity.BasicUser;
import com.ssm.exception.AccountException;
import com.ssm.mapper.BasicUserMapper;

public class ShiroRealm extends AuthorizingRealm{
	
	@Autowired
	private BasicUserMapper basicUserMapper;

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		return null;
	}

	/**
	 * 授权认证
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
		String username = usernamePasswordToken.getUsername();
		String password = String.valueOf(usernamePasswordToken.getPassword());
		BasicUser basicUser = basicUserMapper.selectByUsername(username);
		if(basicUser == null) {
			throw new AccountException("账号或密码错误");
		}
		if(!password.equals(basicUser.getPassword())){
			throw new AccountException("账号或密码错误");
		}
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
				basicUser.getUsername(), basicUser.getPassword(), basicUser.getRealname());
		return simpleAuthenticationInfo;
	}

}

创建一个包Exception 创建类AccountException继承AuthenticationException

package com.ssm.exception;

import org.apache.shiro.authc.AuthenticationException;

public class AccountException extends AuthenticationException{

	private static final long serialVersionUID = 6423461337343398987L;
	
	public AccountException(String msg) {
		super(msg);
	}

}

5.在web.xml文件中配置ShiroFilter

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

6.实现登陆登出功能security/LoginHandler.java,具体实现如下
执行登陆操作的时候,如果处于未登陆状态执行currentUser.login(usernamePasswordToken);跳转到ShiroRealm的doGetAuthenticationInfo方法执行授权认证

package com.ssm.security;

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.ResponseBody;

import com.ssm.exception.AccountException;
import com.ssm.util.EncryptKit;
import com.ssm.util.ResultModel;

@Controller
@RequestMapping("ssm")
public class LoginHandler {

	@RequestMapping("/shirologin")
	@ResponseBody
	public ResultModel shirologin(String username, String password) {
		
		try {
			Subject currentUser = SecurityUtils.getSubject();
			//未认证登录
			if(!currentUser.isAuthenticated()) {
				//密码进行MD5加密
				UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, EncryptKit.MD5(password));
				//认证登陆
				currentUser.login(usernamePasswordToken);
			}
		} catch (AuthenticationException e) {
			if(e instanceof AccountException) {
				return new ResultModel(1, "账号或密码错误");
			}
		}
		return new ResultModel(0, "登陆成功");
	}
	
	/**
	 * 登出
	 * @return
	 */
	@RequestMapping("/shirologout")
	public String shirologout() {
		try {
			Subject subject = SecurityUtils.getSubject();
			if(subject.isAuthenticated()) {
				subject.logout();
				//登出成功
				return "redirect:/login.jsp";
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "redirect:/ssm/home";
	}
}

7.创建主页控制器Controller/HomeController

package com.ssm.controller
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * 主页控制器
 * @author https://blog.csdn.net/caiqing116
 */
@Controller
@RequestMapping("ssm")
public class HomeController {

	@RequestMapping("/home")
	public String home() {
		return "/home";
	}
}

8.创建登陆页面login.jsp
页面中的样式,js等都有对应的静态资源,这里就不一一贴可进入项目git地址下载:
https://github.com/gitcaiqing/mybatis_generator_zh.git,也可以自己简单创建一个form表单页即可测试之

 <%@page import="org.apache.shiro.SecurityUtils"%>
<%@page import="org.apache.shiro.subject.Subject"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@ include file="/WEB-INF/common/taglib.jsp"%>
<%
	//如果登陆成功,则直接跳转到主页
	Subject subject = SecurityUtils.getSubject();
	if(subject.isAuthenticated()){
		response.sendRedirect(request.getContextPath()+"/ssm/home");
	}
%>


登陆











 








 

	

Sign In

<%--
--%>
账号:
密码:
记住我

9创建登陆成功页views/home.jsp

   <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    
    <%@ include file="/WEB-INF/common/taglib.jsp"%>
    
    
    
    Insert title here
    
    
    	

登陆成功

10附MD5加密工具类和ResultModel

package com.ssm.util;

import java.security.MessageDigest;

public class EncryptKit {
	
	private static String MD5 = "MD5";
	private static String SHA = "SHA-1";

	/**
	 * MD5加密
	 * @param string
	 * @return
	 */
	public static String MD5(String string) {
		char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
		try {
			byte[] older = string.getBytes();
			MessageDigest md5 = MessageDigest.getInstance(MD5);
			md5.update(older);
			byte[] newer = md5.digest();
			int j = newer.length;
			char[] chars = new char[j * 2];
			int k = 0;
			for (int i = 0; i < j; i++) {
				byte b = newer[i];
				chars[k++] = hexDigits[b >>> 4 & 0xf];
				chars[k++] = hexDigits[b & 0xf];
			}
			return new String(chars);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * SHA-1加密
	 * @param info
	 * @return
	 */
	public static String SHA(String info) {
		try {
			MessageDigest md = MessageDigest.getInstance(SHA);
			md.update(info.getBytes());
			byte[] digest = md.digest();

			StringBuffer hexstr = new StringBuffer();
			String shaHex = "";
			for (int i = 0; i < digest.length; i++) {
				shaHex = Integer.toHexString(digest[i] & 0xFF);
				if (shaHex.length() < 2) {
					hexstr.append(0);
				}
				hexstr.append(shaHex);
			}
			return hexstr.toString();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
}

ResultModle.java

package com.ssm.util;
/**
 * 返回结果对象
 * @author https://blog.csdn.net/caiqing116
 */
public class ResultModel {

	//返回值 0成功 1失败
	private Integer resultCode;
	//返回的数据
	private Object data;
	//返回的信息
	private String msg;
	public ResultModel(Integer resultCode, String msg) {
		super();
		this.resultCode = resultCode;
		this.msg = msg;
	}
	public ResultModel(Integer resultCode, Object data, String msg) {
		super();
		this.resultCode = resultCode;
		this.data = data;
		this.msg = msg;
	}
	public Integer getResultCode() {
		return resultCode;
	}
	public void setResultCode(Integer resultCode) {
		this.resultCode = resultCode;
	}
	public Object getData() {
		return data;
	}
	public void setData(Object data) {
		this.data = data;
	}
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	
}

你可能感兴趣的:(SSM)