1. pom.xml文件引入依赖
<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. 创建shiro配置类
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();
//设置session的过期时间为1小时,(默认时间时30分钟)
sessionManager.setGlobalSessionTimeout(60*60*1000);
//开启扫描session线程,清理超时会话
sessionManager.setSessionValidationSchedulerEnabled(true);
//禁用了url重写 去掉URL中的JSESSIONID
sessionManager.setSessionIdUrlRewritingEnabled(false);//默认true
return sessionManager;
}
/**
* 创建SecurityManager
*/
@Bean
public SecurityManager securityManager(UserRealm userRealm, SessionManager sessionManager){
//密码加密规则
/*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;
}
/**
* 创建shiroFilter过滤器
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
//anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main -->
//authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
shiroFilter.setLoginUrl("/login.html");//没有认证时跳转到的登陆页
shiroFilter.setSuccessUrl("/index.html");//认证成功跳转到主页
shiroFilter.setUnauthorizedUrl("/unauthorized.json");//未授权时的跳转链接
Map<String,String> filterMap = new LinkedHashMap<>();
filterMap.put("/public/**","anon"); //放行静态资源的路径
filterMap.put("/login.html","anon");
filterMap.put("/sys/login","anon");
filterMap.put("/captcha.jpg","anon");//验证码的图片
//filterMap.put("/**","authc");//authc经过认证才能访问
//角色验证 具有admin角色的用户可以访问
//filterMap.put("/sys/menu/del","roles[admin]");
//权限验证 具有perms[sys:menu:update]可以访问
//filterMap.put("/sys/menu/update","perms[sys:menu:update]");
filterMap.put("/**","user");//通过记住我访问
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
/**
* 创建shiro缓存
* @return
*/
@Bean
public EhCacheManager ehCacheManager(){
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
return ehCacheManager;
}
/**
* ShiroConfig配置文件中开启注解
* 配置三个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 创建自定义Realm类 实现认证和授权
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;
/**
* 认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("认证........");
String usernameInput = (String) token.getPrincipal();
String passwordInput = new String((char[])token.getCredentials());
//查询用户是否存在
SysUser user = sysUserService.queryByUserName(usernameInput);
if(user == null){
throw new UnknownAccountException("账号或密码不正确");
}
//数据库中获取的用户名和密码
String username = user.getUsername();
String password = user.getPassword();
//判断密码是否正确
if(!passwordInput.equals(user.getPassword())){
throw new IncorrectCredentialsException("账号或密码不正确");
}
//判断用户账号是否被锁定
if (user.getStatus() == 0){
throw new LockedAccountException("账号已被锁定,请联系管理员");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, this.getName());
return info;
}
/**
* 授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("授权........");
SysUser user = (SysUser) principals.getPrimaryPrincipal();
Set<String> userPermissions = sysUserService.getUserPermissions(user.getUserId());
SimpleAuthorizationInfo authorInfo = new SimpleAuthorizationInfo();
authorInfo.addStringPermissions(userPermissions);
//角色授权
/*List<String> roleList = Arrays.asList("admin");
authorInfo.addRoles(roleList);*/
//资源授权
/*List<String> permList = Arrays.asList("sys:menu:update");
authorInfo.addStringPermissions(permList);*/
return authorInfo;
}
}
3. 创建service和dao
3.1 SysUserService接口和SysUserServiceImpl实现类
//接口
public interface SysUserService {
/**
* 根据用户名,查询系统用户
*/
SysUser queryByUserName(String username);
}
//实现类
@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;
//超级管理员
if (userId == 1) {
List<SysMenu> menuList = sysMenuMapper.queryListAll();
permsList = new ArrayList<>(menuList.size());
for (SysMenu menu : menuList) {
permsList.add(menu.getPerms());
}
}else {
//普通用户授权
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语句
<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;
/**
* 生成验证码
* @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");
//生成文字验证码
String text = producer.createText();
//生成图片验证码
BufferedImage image = producer.createImage(text);
//保存在session中
ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);
//响应给客户端
ServletOutputStream outputStream = response.getOutputStream();
ImageIO.write(image, "jpg", outputStream);
outputStream.flush();//清空缓冲区
}
/**
* 登陆方法
* @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("验证码不正确");
}
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);
//设置记住我
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("账户验证失败");
}
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配置文件的详细说明
[http://blog.csdn.net/mlitsn/article/details/1909192](http://blog.csdn.net/mlitsn/article/details/1909192)
缓存存活时间和失效时间:
[http://www.cnblogs.com/sprinkle/p/6539086.html](http://www.cnblogs.com/sprinkle/p/6539086.html)
设置缓存的大小
[http://elim.iteye.com/blog/2116749](http://elim.iteye.com/blog/2116749)