1.导入的jar包
net.mingsoft
shiro-freemarker-tags
0.1
org.apache.shiro
shiro-core
org.apache.shiro
shiro-web
org.apache.shiro
shiro-ehcache
org.apache.shiro
shiro-spring
2.在web.xml中加入shiro filter
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
shiroFilter
/*
REQUEST
FORWARD
此过滤器要放在第一个,且名称要与spring-shiro,xml中shiro filter一致
3.在freemarker中加入shiro标签
3.1新建一个FreeMarkerConfigExtend类继承FreeMarkerConfigurer,
package com.business.util;
import java.io.IOException;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import com.jagregory.shiro.freemarker.ShiroTags;
import freemarker.template.Configuration;
import freemarker.template.TemplateException;
public class FreeMarkerConfigExtend extends FreeMarkerConfigurer {
@Override
public void afterPropertiesSet() throws IOException, TemplateException {
super.afterPropertiesSet();
Configuration cfg = this.getConfiguration();
cfg.setSharedVariable("shiro", new ShiroTags());//shiro标签
cfg.setNumberFormat("#");//防止页面输出数字,变成2,000
//可以添加很多自己的要传输到页面的[方法、对象、值]
/*
* 在controller层使用注解再加一层判断
* 注解 @RequiresPermissions("/delete")
*/
/*shiro标签*/
/**
1.游客
<@shiro.guest>
您当前是游客,登录
@shiro.guest>
2.user(已经登录,或者记住我登录)
<@shiro.user>
欢迎[<@shiro.principal/>]登录,退出
@shiro.user>
3.authenticated(已经认证,排除记住我登录的)
<@shiro.authenticated>
用户[<@shiro.principal/>]已身份验证通过
@shiro.authenticated>
4.notAuthenticated(和authenticated相反)
<@shiro.notAuthenticated>
当前身份未认证(包括记住我登录的)
@shiro.notAuthenticated>
5.principal标签(能够取到你在realm中保存的信息比如我存的是ShiroUser对象,取出其中urlSet属性)
<@shiro.principal property="urlSet"/>
6.hasRole标签(判断是否拥有这个角色)
<@shiro.hasRole name="admin">
用户[<@shiro.principal/>]拥有角色admin
@shiro.hasRole>
7.hasAnyRoles标签(判断是否拥有这些角色的其中一个)
<@shiro.hasAnyRoles name="admin,user,member">
用户[<@shiro.principal/>]拥有角色admin或user或member
@shiro.hasAnyRoles>
8.lacksRole标签(判断是否不拥有这个角色)
<@shiro.lacksRole name="admin">
用户[<@shiro.principal/>]不拥有admin角色
@shiro.lacksRole>
9.hasPermission标签(判断是否有拥有这个权限)
<@shiro.hasPermission name="user:add">
用户[<@shiro.principal/>]拥有user:add权限
@shiro.hasPermission>
10.lacksPermission标签(判断是否没有这个权限)
<@shiro.lacksPermission name="user:add">
用户[<@shiro.principal/>]不拥有user:add权限
@shiro.lacksPermission>
**/
}
}
3.2修改spring-mvc-servlet.xml中的freemarker配置
4.新建CustomCredentialsMatcher类继承shiro的SimpleCredentialsMatcher类,这个类作用是自定义密码验证
package com.business.shiro;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import com.business.util.MD5Util;
/**
* Description: 告诉shiro如何验证加密密码,通过SimpleCredentialsMatcher或HashedCredentialsMatcher
* @Author: zh
* @Create Date: 2017-5-9
*/
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
Object tokenCredentials = MD5Util.hmac_md5(String.valueOf(token.getPassword()));
Object accountCredentials = getCredentials(info);
//将密码加密与系统加密后的密码校验,内容一致就返回true,不一致就返回false
return equals(tokenCredentials, accountCredentials);
}
}
5.新建ShiroDbRealm类
package com.business.shiro;
import java.util.List;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.apache.log4j.Logger;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import com.business.dao.UserDao;
import com.business.entity.Menu;
import com.business.entity.Role;
import com.business.entity.User;
import com.business.entity.UserRole;
import com.business.service.sysService.MenuService;
import com.business.service.sysService.RoleService;
import com.business.service.sysService.UserRoleService;
import com.business.service.sysService.UserService;
import com.business.util.SessionUtil;
import com.common.util.BizUtil;
import com.google.common.collect.Sets;
/**
* @description:shiro权限认证
* @author:zhanghao
* @date:2017/5/8 14:51
*/
public class ShiroDbRealm extends AuthorizingRealm {
private static final Logger LOGGER = Logger.getLogger(ShiroDbRealm.class);
@Autowired private UserService userService;
@Autowired private UserDao userDao;
@Autowired private RoleService roleService;
@Autowired private UserRoleService userRoleService;
@Autowired private MenuService menuService;
public ShiroDbRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
super(cacheManager, matcher);
}
/**
* Shiro登录认证(原理:用户提交 用户名和密码 --- shiro 封装令牌 ---- realm 通过用户名将密码查询返回 ---- shiro 自动去比较查询出密码和用户输入密码是否一致---- 进行登陆控制 )
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
LOGGER.info("Shiro开始登录认证");
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
User user = userDao.getByName(token.getUsername());
// 账号不存在
if (user == null) {
return null;
}
// 账号未启用
if (user.getStatus() == 1) {
throw new DisabledAccountException();
}
//将用户信息保存在session中
SessionUtil.addSession(user);
UserRole userRole = userRoleService.getByUserId(user.getId());
Role role = roleService.getById(userRole.getRoleId());
// 读取用户的url和角色
Set roles = Sets.newHashSet(role.getName());
List menuIds = BizUtil.stringToLongList(role.getMenu(), ",");
List
6.自定义shiroUser
package com.business.shiro;
import java.io.Serializable;
import java.util.Set;
/**
* @description:自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息
* @author:zhanghao
* @date:2017/5/9
*/
public class ShiroUser implements Serializable {
private static final long serialVersionUID = -1373760761780840081L;
private Long id;
private final String loginName;
private String name;
private Set urlSet;
private Set roles;
public ShiroUser(String loginName) {
this.loginName = loginName;
}
public ShiroUser(Long id, String loginName, String name, Set urlSet) {
this.id = id;
this.loginName = loginName;
this.name = name;
this.urlSet = urlSet;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set getUrlSet() {
return urlSet;
}
public void setUrlSet(Set urlSet) {
this.urlSet = urlSet;
}
public Set getRoles() {
return roles;
}
public void setRoles(Set roles) {
this.roles = roles;
}
public String getLoginName() {
return loginName;
}
/**
* 本函数输出将作为默认的 输出.
*/
@Override
public String toString() {
return loginName;
}
}
7.两个缓存类
package com.business.shiro.cache;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.apache.log4j.Logger;
import org.apache.shiro.cache.CacheException;
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueWrapper;
/**
* 使用spring-cache作为shiro缓存
* @author L.cm
*
*/
@SuppressWarnings("unchecked")
public class ShiroSpringCache implements org.apache.shiro.cache.Cache {
private static final Logger logger = Logger.getLogger(ShiroSpringCache.class);
private final org.springframework.cache.Cache cache;
public ShiroSpringCache(Cache cache) {
if (cache == null) {
throw new IllegalArgumentException("Cache argument cannot be null.");
}
this.cache = cache;
}
@Override
public V get(K key) throws CacheException {
if (logger.isTraceEnabled()) {
logger.trace("Getting object from cache [" + this.cache.getName() + "] for key [" + key + "]key type:" + key.getClass());
}
ValueWrapper valueWrapper = cache.get(key);
if (valueWrapper == null) {
if (logger.isTraceEnabled()) {
logger.trace("Element for [" + key + "] is null.");
}
return null;
}
return (V) valueWrapper.get();
}
@Override
public V put(K key, V value) throws CacheException {
if (logger.isTraceEnabled()) {
logger.trace("Putting object in cache [" + this.cache.getName() + "] for key [" + key + "]key type:" + key.getClass());
}
V previous = get(key);
cache.put(key, value);
return previous;
}
@Override
public V remove(K key) throws CacheException {
if (logger.isTraceEnabled()) {
logger.trace("Removing object from cache [" + this.cache.getName() + "] for key [" + key + "]key type:" + key.getClass());
}
V previous = get(key);
cache.evict(key);
return previous;
}
@Override
public void clear() throws CacheException {
if (logger.isTraceEnabled()) {
logger.trace("Clearing all objects from cache [" + this.cache.getName() + "]");
}
cache.clear();
}
@Override
public int size() {
return 0;
}
@Override
public Set keys() {
return Collections.emptySet();
}
@Override
public Collection values() {
return Collections.emptySet();
}
@Override
public String toString() {
return "ShiroSpringCache [" + this.cache.getName() + "]";
}
}
package com.business.shiro.cache;
import org.apache.log4j.Logger;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.util.Destroyable;
/**
* 使用spring-cache作为shiro缓存
* 缓存管理器
* @author L.cm
*
*/
public class ShiroSpringCacheManager implements CacheManager, Destroyable {
private static final Logger logger = Logger.getLogger(ShiroSpringCacheManager.class);
private org.springframework.cache.CacheManager cacheManager;
public org.springframework.cache.CacheManager getCacheManager() {
return cacheManager;
}
public void setCacheManager(org.springframework.cache.CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
@Override
public Cache getCache(String name) throws CacheException {
if (logger.isTraceEnabled()) {
logger.trace("Acquiring ShiroSpringCache instance named [" + name + "]");
}
org.springframework.cache.Cache cache = cacheManager.getCache(name);
return new ShiroSpringCache(cache);
}
@Override
public void destroy() throws Exception {
cacheManager = null;
}
}
8.spring-shiro.xml
Shiro安全配置
/login = anon
/favicon.ico = anon
/static/** = anon
/** = user
9.如果要再controller层中加入注解判断,还需在spring-mvc-servlet.xml中加入
10.缓存的两个xml文件ehcache.xml,spring-ehcache.xml
11.applicationContext.xml导入配置文件
12.LoginController控制层方法
package com.business.controller.system;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
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.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.business.controller.BaseController;
import com.business.service.sysService.UserService;
import com.business.util.SessionUtil;
import com.google.common.collect.Maps;
@Controller
public class LoginController extends BaseController {
@Resource
private UserService userService;
@RequestMapping(value = "/login",method=RequestMethod.GET)
public String login() {
return "login";
}
@RequestMapping(value = "/login",method=RequestMethod.POST)
public String login(RedirectAttributes attributes, String username, String password, @RequestParam(value = "online", defaultValue = "0") Integer rememberMe) {
Map map = Maps.newHashMap();
if (StringUtils.isBlank(username)) {
map.put("errorInfo", "用户名不能为空");
}
if (StringUtils.isBlank(password)) {
map.put("errorInfo", "密码不能为空");
}
Subject user = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, username+password);
// 设置记住密码
token.setRememberMe(1 == rememberMe);
try {
user.login(token);
return "redirect:/menu/index";
} catch (UnknownAccountException e) {
map.put("errorInfo", "账号不存在!");
attributes.addFlashAttribute("error", map);
return "redirect:/login";
} catch (DisabledAccountException e) {
map.put("errorInfo", "账号未启用!");
attributes.addFlashAttribute("error", map);
return "redirect:/login";
} catch (IncorrectCredentialsException e) {
map.put("errorInfo", "密码错误!");
attributes.addFlashAttribute("error", map);
return "redirect:/login";
} catch (Throwable e) {
map.put("errorInfo", "登录异常!");
attributes.addFlashAttribute("error", map);
return "redirect:/login";
}
}
@RequestMapping(value = "/logout")
public String logout() {
SessionUtil.removeSession();
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/login";
}
}
12.如何在控制层使用注解,可以在baseController中加入总的异常处理
@ExceptionHandler
public String exception(HttpServletRequest request, Exception e) {
//对异常进行判断做相应的处理
if(e instanceof AuthorizationException){
return "redirect:/logout";
}
return null;
}