SSS+Shiro配置
本文主要概述sss+shiro的配置,其他业务层,持久层方法自行书写。采用SpringBoot快速构建项目,其他Spring、SpringMvc、SpringDataJpa的配置不予叙述。其中ShiroConfig.java、ShiroTagFreemarker.java为配置类放在resources下,角色和权限需添加获取所有角色名和权限名的两个方法,返回为Set<String
>
1、 Pom.xml导入相关Jar包
<properties>
<commons-logging.version>1.2</commons-logging.versio>
<shiro-all.version>1.2.5</shiro-all.version>
<slf4j-log4j12.version>1.7.25</slf4j-log4j12.versi>
</properties>
<!-- shiro 相关jar包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>${shiro-all.version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>${commons-logging.version}</version>
</dependency>
<dependency>
<groupId>net.mingsoft</groupId>
<artifactId>shiro-freemarker-tags</artifactId>
<version>1.0.0</version>
</dependency>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
2、 编写MyRealm.java
package com.gezhi.telecom.realm;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Resource;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import com.gezhi.telecom.adminmag.service.IAdminService;
import com.gezhi.telecom.bean.AccountBean;
import com.gezhi.telecom.bean.AdministratorBean;
import com.gezhi.telecom.bean.RoleBean;
import com.gezhi.telecom.rolepermismag.permissionmag.service.IPermissionService;
import com.gezhi.telecom.rolepermismag.rolemag.service.IRoleService;
import com.gezhi.telecom.usermag.accountingmag.service.IAccountingService;
/**
* 数据源realm
* @author Administrator
*
*/
public class DataBaseRealm extends AuthorizingRealm {
@Resource
private IAdminService adminServiceImpl;
@Resource
private IAccountingService accountingServiceImpl;
@Resource
private IRoleService roleServiceImpl;
@Resource
private IPermissionService permissionServiceImpl;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
// TODO Auto-generated method stub
// 能进入到这里,表示账号已经通过验证了
String name = (String) arg0.getPrimaryPrincipal();
AdministratorBean admin=adminServiceImpl.findAdminByAccount(name);
Set<String> roles = new HashSet<>();
RoleBean role=null;
if(admin!=null) {
role=roleServiceImpl.findRoleBeanById(admin.getRole().getId());
}else {
AccountBean account=accountingServiceImpl.findAccountBeanByAccount(name);
if(account!=null) {
role=roleServiceImpl.findRoleBeanById(account.getRole().getId());
}
}
// 获取账号的角色和权限信息(请在业务层书写获取权限名和角色名的方法,返回都为Set<String>)
roles.add(role.getRoleName());
List permission=permissionServiceImpl.findPermissionNameByRoleId(role.getId());
Set<String> permissions = new HashSet<>(permission);
System.out.println(permissions);
// 授权对象信息
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setStringPermissions(permissions);
authorizationInfo.setRoles(roles);
return authorizationInfo;
}
//验证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
// TODO Auto-generated method stub
UsernamePasswordToken upToken = (UsernamePasswordToken) arg0;
String name = upToken.getPrincipal().toString();
// 获取指定用户数据库中的密码,盐
AdministratorBean admin=adminServiceImpl.findAdminByAccount(name);
String pwdInDB="";
String salt="";
if(admin!=null) {
pwdInDB = admin.getPassword();
salt = admin.getPasswordSalt();
}else {
AccountBean account=accountingServiceImpl.findAccountBeanByAccount(name);
if(account!=null) {
pwdInDB = account.getPassword();
salt = account.getSalt();
}
}
// 认证信息,存放账号,密码,盐,getName() 是当前 Realm 的继承方法,通常返回当前类名 :databaseRealm
// 通过 applicationContext-shiro.xml 里配置的 HashedCredentialsMatcher 进行自动校验
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
name,pwdInDB, ByteSource.Util.bytes(salt),getName());
return authenticationInfo;
}
}
3、 书写配置类ShiroConfig.java
package com.gezhi.telecom.config;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.gezhi.telecom.realm.DataBaseRealm;
/**
*
* @author Administrator
*
*/
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shirFilter(org.apache.shiro.mgt.SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//拦截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/system/login", "anon");
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
//<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/**", "authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login.ftl"页面
shiroFilterFactoryBean.setLoginUrl("/sys/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权界面;
// shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public DataBaseRealm myShiroRealm(){
DataBaseRealm myShiroRealm = new DataBaseRealm();
return myShiroRealm;
}
// /**
// * 凭证匹配器
// * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
// * )
// *
// * @return
// */
// @Bean
// public HashedCredentialsMatcher hashedCredentialsMatcher() {
// HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
// hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
// return hashedCredentialsMatcher;
//
// }
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
}
**
4、 编写密码加密工具类
package com.gezhi.telecom.util;
import org.apache.shiro.crypto.hash.SimpleHash;
import com.gezhi.telecom.bean.AccountBean;
import com.gezhi.telecom.bean.AdministratorBean;
/**
* 密码加密工具类
* @author Administrator
*
*/
public class PasswordEncryptionUtil {
/**
* 加密
* @param algorithmName 加密算法名称
* @param hashIterations 迭代次数
* @param user 加密对象
* @return 加密后的对象
*/
public static Object AllPasswordEncryption(String algorithmName,int hashIterations,Object obj) {
String salt = "cjwdzkyxz"; // 盐值
if(obj instanceof AdministratorBean) {
AdministratorBean admin=(AdministratorBean) obj;
//获得用户输入的密码
String password=admin.getPassword();
//通过SimpleHash使用散列算法和盐值得到最终加密密码
String encodePassword=new SimpleHash(algorithmName, password, salt, hashIterations).toString();
admin.setPassword(encodePassword);
admin.setPasswordSalt(salt);
return admin;
}else {
AccountBean account=(AccountBean) obj;
String password=account.getPassword();
//通过SimpleHash使用散列算法和盐值得到最终加密密码
String encodePassword=new SimpleHash(algorithmName, password, salt, hashIterations).toString();
account.setPassword(encodePassword);
account.setSalt(salt);
return account;
}
}
}
**
5、 在Controller使用密码加密
@ResponseBody
@RequestMapping(value = "/{id}", method = RequestMethod.POST, produces = "application/json;charset=utf-8")
public ResMessage saveAccountBean(AccountBean accoun) {
ResMessage res = new ResMessage(true, "操作成功!");
try {
accoun.setId(null);
accoun.setCreateTime(new Date());
PasswordEncryptionUtil.AllPasswordEncryption("md5", 2, accoun);//调用密码加密工具类
AccountingServiceImpl.addAccounting(accoun);
} catch (Exception e) {
// TODO: handle exception
res.setStatus(false);
res.setInfo("系统繁忙,请稍后重试!");
}
return res;
}
6、 编写登录Controller
package com.gezhi.telecom.rolepermismag.controller;
import javax.annotation.Resource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
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 com.gezhi.telecom.adminmag.service.IAdminService;
import com.gezhi.telecom.bean.AccountBean;
import com.gezhi.telecom.bean.AdministratorBean;
import com.gezhi.telecom.usermag.accountingmag.service.IAccountingService;
@RequestMapping("/system")
@Controller
public class LoginController {
@Resource
private IAccountingService accountingServiceImpl;
@Resource
private IAdminService adminServiceImpl;
/**
* 登录,获取账号密码进行验证
*/
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(Model model, String username, String password) {
Subject subject = SecurityUtils.getSubject();//获得当前用户对象
String salt = "cjwdzkyxz"; // 盐值
String encodePassword=new SimpleHash("md5", password, salt, 2).toString();//加密密码
UsernamePasswordToken token = new UsernamePasswordToken(username, encodePassword);
try {
subject.login(token);
Session session = subject.getSession();
String userName=(String) subject.getPrincipal();
System.out.println("欢迎登录!");
session.setAttribute("subject", subject);
session.setAttribute("userName", userName);
AccountBean acc=accountingServiceImpl.findAccountBeanByAccount(userName);
Byte by;
if(acc!=null) {
by=acc.getRole().getRoleByte();
}else {
AdministratorBean admin=adminServiceImpl.findAdminByAccount(userName);
System.out.println(admin);
by=admin.getRole().getRoleByte();
}
String roleType="";
if(by==0) {
roleType="超级管理员";
}else if(by==1) {
roleType="普通管理员";
}else {
roleType="用户";
}
session.setAttribute("roleType", roleType);
return "/index";
}catch(UnknownAccountException e) {
System.out.println("用户名或密码错误");
return "/login";
}catch(IncorrectCredentialsException e) {
System.out.println("用户名或密码错误");
return "/login";
}
// model.addAttribute("error", "验证失败");
}
/**
* 退出
* @param model
* @param username
* @param password
* @return
*/
@RequestMapping(value = "/loginout", method = RequestMethod.GET)
public String loginOut(Model model, String username, String password) {
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "/login";
}
}
7、 编写ShiroTagFreemarker.java ftl引入shiro标签
package com.gezhi.telecom.config;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import com.jagregory.shiro.freemarker.ShiroTags;
import freemarker.template.TemplateModelException;
/**
* 用于ftl引入shiro标签
* @author Administrator
*
*/
@Configuration
public class ShiroTagFreemarkerConfig {
@Resource
private FreeMarkerConfigurer freeMarkerConfigurer;
@PostConstruct
public void setSharedVariable() throws TemplateModelException {
freeMarkerConfigurer.getConfiguration().setSharedVariable("shiro", new ShiroTags());
}
}
8、 使用<@shiro.hasPermission name=“用户管理”></@shiro.hasPermission> 标签判断是否拥有权限
<@shiro.hasPermission name="用户管理">
<dl id="menu-product">
<dt><i class="Hui-iconfont"></i> 用户管理系统<i class="Hui-iconfont menu_dropdown-arrow"></i></dt>
<dd>
<ul>
<li><a data-href="administratorAccount.html" data-title="管理员管理" href="javascript:void(0)">管理员管理</a></li>
<li><a data-href="businessAccount.html" data-title="业务账号管理" href="javascript:void(0)">业务账号管理</a></li>
<li><a data-href="accountingAccount.html" data-title="账务账号管理" href="javascript:void(0)">账务账号管理</a></li>
</ul>
</dd>
</dl>
</@shiro.hasPermission>