整合shiro以及将session存入reids中,导入shiro-redis包,就不用自己实现怎么往redis中存session了。现在一般都是前后端分离的项目,后台返回统一的格式给前端
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:域名注册、网页空间、营销软件、网站建设、莱阳网站维护、网站推广。
pom
xsi:schemaLocation=
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.6.RELEASE
com.pwl
springboot-shiro
0.0.1-SNAPSHOT
springboot-shiro
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.apache.shiro
shiro-spring-boot-starter
1.4.0
MySQL
mysql-connector-java
com.baomidou
mybatis-plus-boot-starter
2.2.0
com.zaxxer
HikariCP
org.apache.velocity
velocity-engine-core
2.0
org.crazycake
shiro-redis
2.4.2.1-RELEASE
org.apache.shiro
shiro-core
redis.clients
jedis
2.7.2
org.springframework.boot
spring-boot-maven-plugin
认证类,我用的是mybatis-plus
package com.pwl.shiro.ream;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.pwl.shiro.entity.SysUser;
import com.pwl.shiro.service.SysPermissionService;
import com.pwl.shiro.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.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
/**
* @author Pan Weilong
* @date 2019/6/20 20:11
* @description: 接口.
*/
public class UserRealm extends AuthorizingRealm{
private static final Logger LOGGER = LoggerFactory.getLogger(UserRealm.class);
@Autowired
private SysUserService sysUserService;
@Autowired
private SysPermissionService sysPermissionService;
/**
* 授权
*
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SysUser sysUser = (SysUser) principals.getPrimaryPrincipal();
//List sysPermissions = sysPermissionService.selectPermissionByUserId(sysUser.getUserId());
List sysPermissions=new ArrayList<>();
sysPermissions.add("systemUserAdd");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(sysPermissions);
LOGGER.info("doGetAuthorizationInfo");
return info;
}
/**
* 认证
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
Wrapper objectWrapper = new EntityWrapper<>();
objectWrapper.eq("user_name",token.getUsername());
SysUser sysUser = sysUserService.selectOne(objectWrapper);
if (sysUser == null) {
return null;
}
LOGGER.info("doGetAuthenticationInfo");
return new SimpleAuthenticationInfo(sysUser, sysUser.getPassword().toCharArray(), ByteSource.Util.bytes(sysUser.getSalt()), getName());
}
}
shiro配置类,很重要
package com.pwl.shiro.config;
import com.pwl.shiro.ream.UserRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author Pan Weilong
* @date 2019/6/20 20:10
* @description: 接口.
*/
@Configuration
public class ShiroConfig {
/**
* 凭证匹配器
*
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//md5加密
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//加密1次
hashedCredentialsMatcher.setHashIterations(1);
return hashedCredentialsMatcher;
}
/**
* 自定义realm
*
* @return
*/
@Bean
public UserRealm userRealm() {
UserRealm userRealm = new UserRealm();
userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return userRealm;
}
/**
* 安全管理器
* 注:使用shiro-spring-boot-starter 1.4时,返回类型是SecurityManager会报错,直接引用shiro-spring则不报错
*
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm());
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* @Author
* @Description redis缓存
* @Date 21:32 2019/6/23
* @Param []
* @return org.crazycake.shiro.RedisManager
**/
@Bean
public RedisManager redisManager(){
RedisManager redisManager = new RedisManager();
redisManager.setHost("127.0.0.1");
redisManager.setPort(6379);
//失效时间30分钟
redisManager.setExpire(1800);
return redisManager;
}
@Bean
public RedisSessionDAO redisSessionDAO(){
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
//存入redis前缀
redisSessionDAO.setKeyPrefix("redis_");
return redisSessionDAO;
}
@Bean
public RedisCacheManager redisCacheManager(){
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
@Bean
public SessionManager sessionManager() {
SessionManager sessionManager =new ShiroSessionManager();
//设置过期时间ms
((DefaultWebSessionManager) sessionManager).setGlobalSessionTimeout(1800000);
//删除无效的session
((DefaultWebSessionManager) sessionManager).setDeleteInvalidSessions(Boolean.TRUE);
//重写url
((DefaultWebSessionManager) sessionManager).setSessionIdUrlRewritingEnabled(Boolean.TRUE);
SimpleCookie simpleCookie = new SimpleCookie();
simpleCookie.setName("loginUser");
//设置cookie
((DefaultWebSessionManager) sessionManager).setSessionIdCookie(simpleCookie);
((DefaultWebSessionManager) sessionManager).setSessionDAO(redisSessionDAO());
((DefaultWebSessionManager) sessionManager).setCacheManager(redisCacheManager());
return sessionManager;
}
/**
* 设置过滤规则
*
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setSuccessUrl("/");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");
//注意此处使用的是LinkedHashMap,是有顺序的,shiro会按从上到下的顺序匹配验证,匹配了就不再继续验证
//所以上面的url要苛刻,宽松的url要放在下面,尤其是"/**"要放到最下面,如果放前面的话其后的验证规则就没作用了。
Map filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/captcha.jpg", "anon");
filterChainDefinitionMap.put("/favicon.ico", "anon");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
}
为了避免session频繁从redis中读取,要重写方法
package com.pwl.shiro.config;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import javax.servlet.ServletRequest;
import java.io.Serializable;
/**
* @author Pan Weilong
* @date 2019/6/22 10:40
* @description: 接口.
*/
public class ShiroSessionManager extends DefaultWebSessionManager {
@Override
protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
Serializable sessionId = getSessionId(sessionKey);
ServletRequest request = null;
if (sessionKey instanceof WebSessionKey) {
request = ((WebSessionKey) sessionKey).getServletRequest();
}
if (request != null && null != sessionId) {
Object sessionObj = request.getAttribute(sessionId.toString());
if (sessionObj != null) {
return (Session) sessionObj;
}
}
Session session = super.retrieveSession(sessionKey);
if (request != null && null != sessionId) {
request.setAttribute(sessionId.toString(), session);
}
return session;
}
}
统一异常处理类
package com.pwl.shiro.exception;
import com.pwl.shiro.common.ResultVO;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
/**
* @Author Pan Weilong
* @Description 全局异常捕获
* @Date 15:11 2019/6/20
* @Param
* @return
**/
@ControllerAdvice
public class GlobalExceptionHandler implements ApplicationContextAware {
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
private ApplicationContext applicationContext;
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResultVO defaultErrorHandler(HttpServletRequest request, Exception e) throws Exception {
if(e instanceof UnauthenticatedException){
return new ResultVO().returnFail(401,"认证失败");
}else if(e instanceof UnauthorizedException){
return new ResultVO().returnFail(401,"无权限访问");
}
return new ResultVO().returnFail(e.getMessage());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
返回统一的格式
package com.pwl.shiro.common;
import java.io.Serializable;
/**
* @author Pan Weilong
* @date 2019/6/20 15:03
* @description: 结果统一返回
*/
public class ResultVO implements Serializable {
private static final long serialVersionUID = 1L;
public static final int SUCCESS = 200;
public static final int FAIL = 1;
private String msg = "success";
private int code = SUCCESS;
private T data;
public ResultVO() {
super();
}
public ResultVO(T data) {
super();
this.data = data;
}
public ResultVO(T data, String msg) {
super();
this.data = data;
this.msg = msg;
}
public ResultVO(Throwable e) {
super();
this.msg = e.getMessage();
this.code = FAIL;
}
/**
*
* 返回成功
* @param data
* @return
*/
public ResultVO returnSuccess(T data) {
this.data = data;
return this;
}
/**
*无锡人流医院 http://www.bhnkyy39.com/
* 返回失败
*
* @param code
* @param msg
* @return
*/
public ResultVO returnFail(Integer code , String msg) {
this.code = code;
this.msg = msg;
return this;
}
public ResultVO returnFail(String msg) {
this.code = 500;
this.msg = msg;
return this;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
controller
package com.pwl.shiro.controller;
import com.pwl.shiro.common.ResultVO;
import com.pwl.shiro.entity.SysUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @author Pan Weilong
* @date 2019/6/20 21:00
* @description: 接口.
*/
@RestController
public class LonginController {
@GetMapping("/login")
public ResultVO login(HttpServletRequest request){
return new ResultVO().returnFail(401,"认证失败");
}
@PostMapping("/login")
public ResultVO login(@RequestBody SysUser sysUser) {
Subject user = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(sysUser.getUserName(), sysUser.getPassword());
try {
//shiro帮我们匹配密码什么的,我们只需要把东西传给它,它会根据我们在UserRealm里认证方法设置的来验证
user.login(token);
} catch (Exception e) {
e.printStackTrace();
throw new UnauthenticatedException();
}
return new ResultVO("登录成功");
}
}
package com.pwl.shiro.controller;
import com.pwl.shiro.common.ResultVO;
import com.pwl.shiro.entity.SysUser;
import com.pwl.shiro.service.SysUserService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
*
* 前端控制器
*
*
* @author pwl
* @since 2019-06-20
*/
@RestController
@RequestMapping("/sysUser")
public class SysUserController {
@Autowired
private SysUserService sysUserService;
//需要有systemUser权限才能访问
@RequiresPermissions("systemUserAdd")
@GetMapping
public ResultVO getUserList(){
List sysUsers = sysUserService.selectList(null);
return new ResultVO(sysUsers);
}
//需要有add权限才能访问
@RequiresPermissions("Add")
@GetMapping("/getList")
public ResultVO getList(){
List sysUsers = sysUserService.selectList(null);
return new ResultVO(sysUsers);
}
}
首次登陆的时候
然后登陆
最后访问需要某些权限的接口
当用户没有权限的时候访问接口
贴的部分代码
sql及项目地址