1. pom.xml文件引入依賴(lài)
<properties>
<shiro.version>1.3.2</shiro.version>
</properties>
<!-- 集成shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- shiro緩存 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
2. 創(chuàng)建shiro配置類(lèi)
import com.qfedu.rongzaiboot.shiro.UserRealm;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* shiro配置
*/
@Configuration
public class ShiroConfig {
/**
* session管理器
* @return
*/
@Bean(name = "sessionManager")
public SessionManager sessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//設(shè)置session的過(guò)期時(shí)間為1小時(shí),(默認(rèn)時(shí)間時(shí)30分鐘)
sessionManager.setGlobalSessionTimeout(60*60*1000);
//開(kāi)啟掃描session線(xiàn)程朗兵,清理超時(shí)會(huì)話(huà)
sessionManager.setSessionValidationSchedulerEnabled(true);
//禁用了url重寫(xiě) 去掉URL中的JSESSIONID
sessionManager.setSessionIdUrlRewritingEnabled(false);//默認(rèn)true
return sessionManager;
}
/**
* 創(chuàng)建SecurityManager
*/
@Bean
public SecurityManager securityManager(UserRealm userRealm, SessionManager sessionManager){
//密碼加密規(guī)則
/*HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
credentialsMatcher.setHashIterations(1024);
//credentialsMatcher.setHashSalted(true);
userRealm.setCredentialsMatcher(credentialsMatcher);*/
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
securityManager.setSessionManager(sessionManager);
securityManager.setCacheManager(ehCacheManager());
return securityManager;
}
/**
* 創(chuàng)建shiroFilter過(guò)濾器
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
//anon:它對(duì)應(yīng)的過(guò)濾器里面是空的,什么都沒(méi)做,這里.do和.jsp后面的*表示參數(shù),比方說(shuō)login.jsp?main -->
//authc:該過(guò)濾器下的頁(yè)面必須驗(yàn)證后才能訪(fǎng)問(wèn),它是Shiro內(nèi)置的一個(gè)攔截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
shiroFilter.setLoginUrl("/login.html");//沒(méi)有認(rèn)證時(shí)跳轉(zhuǎn)到的登陸頁(yè)
shiroFilter.setSuccessUrl("/index.html");//認(rèn)證成功跳轉(zhuǎn)到主頁(yè)
shiroFilter.setUnauthorizedUrl("/unauthorized.json");//未授權(quán)時(shí)的跳轉(zhuǎn)鏈接
Map<String,String> filterMap = new LinkedHashMap<>();
filterMap.put("/public/**","anon"); //放行靜態(tài)資源的路徑
filterMap.put("/login.html","anon");
filterMap.put("/sys/login","anon");
filterMap.put("/captcha.jpg","anon");//驗(yàn)證碼的圖片
//filterMap.put("/**","authc");//authc經(jīng)過(guò)認(rèn)證才能訪(fǎng)問(wèn)
//角色驗(yàn)證 具有admin角色的用戶(hù)可以訪(fǎng)問(wèn)
//filterMap.put("/sys/menu/del","roles[admin]");
//權(quán)限驗(yàn)證 具有perms[sys:menu:update]可以訪(fǎng)問(wèn)
//filterMap.put("/sys/menu/update","perms[sys:menu:update]");
filterMap.put("/**","user");//通過(guò)記住我訪(fǎng)問(wèn)
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
/**
* 創(chuàng)建shiro緩存
* @return
*/
@Bean
public EhCacheManager ehCacheManager(){
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
return ehCacheManager;
}
/**
* ShiroConfig配置文件中開(kāi)啟注解
* 配置三個(gè)bean:
* lifecycleBeanPostProcessor
* defaultAdvisorAutoProxyCreator
* authorizationAttributeSourceAdvisor
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(true);
return proxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
2.1 創(chuàng)建自定義Realm類(lèi) 實(shí)現(xiàn)認(rèn)證和授權(quán)
package com.qfedu.rongzaiboot.shiro;
import com.qfedu.rongzaiboot.entity.SysUser;
import com.qfedu.rongzaiboot.service.SysUserService;
import org.apache.shiro.authc.*;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Set;
@Component
public class UserRealm extends AuthorizingRealm {
@Autowired
private SysUserService sysUserService;
/**
* 認(rèn)證
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("認(rèn)證........");
String usernameInput = (String) token.getPrincipal();
String passwordInput = new String((char[])token.getCredentials());
//查詢(xún)用戶(hù)是否存在
SysUser user = sysUserService.queryByUserName(usernameInput);
if(user == null){
throw new UnknownAccountException("賬號(hào)或密碼不正確");
}
//數(shù)據(jù)庫(kù)中獲取的用戶(hù)名和密碼
String username = user.getUsername();
String password = user.getPassword();
//判斷密碼是否正確
if(!passwordInput.equals(user.getPassword())){
throw new IncorrectCredentialsException("賬號(hào)或密碼不正確");
}
//判斷用戶(hù)賬號(hào)是否被鎖定
if (user.getStatus() == 0){
throw new LockedAccountException("賬號(hào)已被鎖定污淋,請(qǐng)聯(lián)系管理員");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, this.getName());
return info;
}
/**
* 授權(quán)
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("授權(quán)........");
SysUser user = (SysUser) principals.getPrimaryPrincipal();
Set<String> userPermissions = sysUserService.getUserPermissions(user.getUserId());
SimpleAuthorizationInfo authorInfo = new SimpleAuthorizationInfo();
authorInfo.addStringPermissions(userPermissions);
//角色授權(quán)
/*List<String> roleList = Arrays.asList("admin");
authorInfo.addRoles(roleList);*/
//資源授權(quán)
/*List<String> permList = Arrays.asList("sys:menu:update");
authorInfo.addStringPermissions(permList);*/
return authorInfo;
}
}
3. 創(chuàng)建service和dao
3.1 SysUserService接口和SysUserServiceImpl實(shí)現(xiàn)類(lèi)
//接口
public interface SysUserService {
/**
* 根據(jù)用戶(hù)名,查詢(xún)系統(tǒng)用戶(hù)
*/
SysUser queryByUserName(String username);
}
//實(shí)現(xiàn)類(lèi)
@Service
public class SysUserServiceImpl implements SysUserService {
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private SysMenuMapper sysMenuMapper;
@Override
public SysUser queryByUserName(String username) {
return sysUserMapper.queryByUserName(username);
}
@Override
public Set<String> getUserPermissions(Long userId) {
List<String> permsList = null;
//超級(jí)管理員
if (userId == 1) {
List<SysMenu> menuList = sysMenuMapper.queryListAll();
permsList = new ArrayList<>(menuList.size());
for (SysMenu menu : menuList) {
permsList.add(menu.getPerms());
}
}else {
//普通用戶(hù)授權(quán)
permsList = sysUserMapper.queryAllPerms(userId);
}
Set<String> permsSet = new HashSet<>();
for (String perms : permsList) {
if (StringUtils.isBlank(perms)) {
continue;
}
permsSet.addAll(Arrays.asList(perms.trim().split(",")));
}
return permsSet;
}
}
3.2 mapper的sql語(yǔ)句
<select id="queryByUserName" resultType="com.qfedu.rongzaiboot.entity.SysUser">
select * from sys_user where username = #{username}
</select>
<select id="queryAllPerms" resultType="java.lang.String">
SELECT perms FROM sys_menu m
LEFT JOIN sys_role_menu rm ON m.menu_id = rm.menu_id
LEFT JOIN sys_user_role ur ON ur.role_id = rm.role_id
WHERE ur.user_id = #{userId}
</select>
<select id="queryListAll" resultType="com.qfedu.rongzaiboot.entity.SysMenu">
select menu_id,name,url,perms,type,icon,order_num,(select p.name from sys_menu p where p.menu_id = m.parent_id) as parentName
from sys_menu m order by m.order_num asc
</select>
4. LoginController中添加登錄controller方法
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import com.qfedu.rongzaiboot.utils.R;
import com.qfedu.rongzaiboot.utils.ShiroUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Map;
@Controller
public class SysLoginController {
@Autowired
private Producer producer;
/**
* 生成驗(yàn)證碼
* @param response
* @throws IOException
*/
@RequestMapping("/captcha.jpg")
public void kaptcha(HttpServletResponse response) throws IOException {
//避免瀏覽器緩存
response.setHeader("Cache-Control", "no-store,no-cache");
response.setContentType("image/jpeg");
//生成文字驗(yàn)證碼
String text = producer.createText();
//生成圖片驗(yàn)證碼
BufferedImage image = producer.createImage(text);
//保存在session中
ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);
//響應(yīng)給客戶(hù)端
ServletOutputStream outputStream = response.getOutputStream();
ImageIO.write(image, "jpg", outputStream);
outputStream.flush();//清空緩沖區(qū)
}
/**
* 登陸方法
* @param map
* @return
*/
@ResponseBody
@RequestMapping("/sys/login")
public R login(@RequestBody Map<String,String> map) {
String username = map.get("username");
String password = map.get("password");
String captcha = map.get("captcha");
String rememberMe = map.get("rememberMe");
String sessionCaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);
if (!captcha.equalsIgnoreCase(sessionCaptcha)) {
return R.error("驗(yàn)證碼不正確");
}
boolean remember = false;
if (rememberMe != null){
remember = true;
}
Subject subject = ShiroUtils.getSubject();
try {
password = new Md5Hash(password,username,1024).toHex();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//設(shè)置記住我
token.setRememberMe(remember);
subject.login(token);
}catch (UnknownAccountException e){
return R.error(e.getMessage());
}catch (IncorrectCredentialsException e){
return R.error(e.getMessage());
}catch (LockedAccountException e){
return R.error(e.getMessage());
}catch (AuthenticationException e){
return R.error("賬戶(hù)驗(yàn)證失敗");
}
return R.ok();
}
/**
* 退出方法
* @return
*/
@GetMapping("/logout")
public String logout(){
ShiroUtils.logout();
return "redirect:login.html";
}
}
5. shiro緩存的配置 文件ehcache.xml
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxEntriesLocalHeap="2000"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true" />
</ehcache>
Ehcache配置文件的詳細(xì)說(shuō)明
[http://blog.csdn.net/mlitsn/article/details/1909192](http://blog.csdn.net/mlitsn/article/details/1909192)
緩存存活時(shí)間和失效時(shí)間:
[http://www.cnblogs.com/sprinkle/p/6539086.html](http://www.cnblogs.com/sprinkle/p/6539086.html)
設(shè)置緩存的大小
[http://elim.iteye.com/blog/2116749](http://elim.iteye.com/blog/2116749)