spring-boot集成8:集成shiro,jwt
Shrio是一个轻量级的,基于AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。
JWT(JSON Web Token)是目前最流行的跨域身份验证解决方案,具有加密和自包含的特性。
1.maven配置
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.</version>
</dependency> <!--jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.2.</version>
</dependency>
2.自定义Token Authentication
package com.bby.security; import org.apache.shiro.authc.AuthenticationToken; /**
* 自定义jwt类型的token
*
* @author: zhangyang
* @create: 2018/11/28 8:39
**/
public class MyJWTToken implements AuthenticationToken {
private String token; public MyJWTToken(String token) {
this.token = token;
} @Override
public Object getPrincipal() {
return token;
} @Override
public Object getCredentials() {
return token;
}
}
3.自定义Shiro过滤器
package com.bby.security; import com.alibaba.fastjson.JSONObject;
import com.bby.common.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.util.AntPathMatcher;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter; /**
* jwt token filter
*
* @Author zhangyang
* @Date 下午 8:42 2018/11/27 0027
**/
@Slf4j
public class MyJWTFilter extends BasicHttpAuthenticationFilter { private String tokenHeader;
private String loginUri; public MyJWTFilter(String tokenHeader, String loginUri) {
this.tokenHeader = tokenHeader;
this.loginUri = loginUri;
} /**
* 如果是登录则直接放行;
* 如果带有 token,则对 token 进行检查
*
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
HttpServletRequest req = (HttpServletRequest) request;
AntPathMatcher matcher = new AntPathMatcher();
// 开放登录接口访问
if (matcher.match(loginUri, req.getRequestURI())) {
return true;
}
// 判断请求的请求头是否带上token
if (StringUtils.isBlank(req.getHeader(tokenHeader))) {
return false;
} // 如果存在,则进入 executeLogin 方法执行登入,检查 token 是否正确
try {
executeLogin(request, response);
} catch (Exception e) {
log.error(e.getMessage());
try {
// globalExceptionHandler无法处理filter中的异常,这里手动处理
PrintWriter out = response.getWriter();
out.print(JSONObject.toJSON(Result.failureWithCode(Result.UNAUTHORIZED, e.getMessage())));
out.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
return false;
}
return true;
} /**
* 执行登陆操作
*
* @param request
* @param response
* @return
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader(tokenHeader);
MyJWTToken jwtToken = new MyJWTToken(token);
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
getSubject(request, response).login(jwtToken);
// 如果没有抛出异常则代表登入成功,返回true
return true;
}
}
4.自定义Shiro Realm
package com.bby.security; import com.bby.common.util.RedisRepository;
import com.bby.mapper.system.SysUserMapper;
import org.apache.commons.collections4.CollectionUtils;
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.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.List; /**
* 实现AuthorizingRealm接口用户用户认证
*
* @author: zhangyang
* @create: 2018/11/24 21:25
**/
@Component
public class MyRealm extends AuthorizingRealm { @Autowired
private RedisRepository redisRepository; @Autowired
private SysUserMapper sysUserMapper; /**
* 获取用户权限信息
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取token
String token = (String) principalCollection.getPrimaryPrincipal();
// 查询用户权限信息
List<String> permissions = sysUserMapper.getPermissionByUserId(JWTUtil.getUserId(token)); // 只添加权限(角色方式不灵活)
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
if (CollectionUtils.isNotEmpty(permissions)) {
simpleAuthorizationInfo.addStringPermissions(permissions);
}
return simpleAuthorizationInfo;
} /**
* 获取用户认证信息
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 加这一步的目的是在Post请求的时候会先进认证,然后在到请求
Object principal = authenticationToken.getPrincipal();
if (principal == null) {
return null;
} String token = (String) principal;
// 解密获得username,用于和数据库进行对比
String claim = JWTUtil.getUserId(token);
// 验证缓存中的登录状态
if (claim == null
|| !JWTUtil.verify(token, claim)
|| !redisRepository.exists(token)) {
throw new AuthenticationException("token validation failed");
} // 获取用户信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(token, token, getName());
return simpleAuthenticationInfo;
} /**
* 设置支持的token类型为自定义jwtToken
*
* @param token
* @return
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof MyJWTToken;
} /**
* 覆盖验证密码是否匹配的方法,因为在自定义的login方法中已经实现了
*
* @param token
* @param info
* @throws AuthenticationException
*/
@Override
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
}
}
5.登录代码
package com.bby.controller; import com.bby.common.vo.Result;
import com.bby.security.JWTUtil;
import com.bby.service.system.ISysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest;
import java.util.Map; /**
* 登录/鉴权controller
*
* @author: zhangyang
* @create: 2018/11/25 20:26
**/
@Api(tags = "认证授权")
@RestController
@RequestMapping("auth")
public class AuthController { @Autowired
private ISysUserService sysUserService; /**
* 登录
*
* @param authInfo
* @return
*/
@ApiOperation("登录")
@PostMapping("login")
public Result login(@ApiParam("用户名") @RequestBody Map<String, String> authInfo) {
return sysUserService.validate(authInfo.get("username"), authInfo.get("password"));
} /**
* 登出
*
* @return
*/
@ApiOperation("登出")
@PostMapping("logout")
public Result logout(HttpServletRequest request) {
// 因为token的方式是无状态的,要实现登出,则需要使用缓存来保存状态
return sysUserService.logout(JWTUtil.getToken(request));
} /**
* 用户信息
* 用户名:
* @return
*/
@ApiOperation("获取用户信息")
@GetMapping("info")
public Result info(HttpServletRequest request) {
return sysUserService.getAuthInfo(JWTUtil.getUserId(JWTUtil.getToken(request)));
}
}
6.shiro配置
package com.bby.security; import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn; import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map; /**
* shiro 配置
*
* @author: zhangyang
* @create: 2018/11/24 21:34
**/
@Configuration
public class ShiroConfiguration {
@Value("${jwt.token-header}")
private String tokenHeader; @Value("${jwt.filter-name}")
private String jwtFilterName; @Value("${login.uri}")
private String loginUri; /**
* 将自己的验证方式加入容器
*
* @return
*/
@Autowired
private MyRealm myRealm; /**
* 权限管理,配置主要是Realm的管理认证
*
* @return
*/
@Bean
@DependsOn("myRealm")
public SecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
// 使用自己的realm
System.out.println(myRealm);
manager.setRealm(myRealm); // 关闭shiro自带的session
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
manager.setSubjectDAO(subjectDAO);
return manager;
} /**
* Filter工厂,设置对应的过滤条件和跳转条件
*
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<>();
System.out.println(tokenHeader);
filterMap.put("jwt", new MyJWTFilter(tokenHeader, loginUri));
factoryBean.setFilters(filterMap); factoryBean.setSecurityManager(securityManager);
factoryBean.setUnauthorizedUrl("/401"); Map<String, String> filterRuleMap = new HashMap<>();
// 所有请求通过我们自己的JWT Filter
filterRuleMap.put("/**", "jwt");
// 访问401和404页面不通过我们的Filter
filterRuleMap.put("/401", "anon");
factoryBean.setFilterChainDefinitionMap(filterRuleMap);
return factoryBean;
} /**
* 下面的代码是添加注解支持
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// 强制使用cglib,防止重复代理和可能引起代理出错的问题
// https://zhuanlan.zhihu.com/p/29161098
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
} @Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
spring-boot集成8:集成shiro,jwt的更多相关文章
- Spring Boot 最简单整合Shiro+JWT方式
简介 目前RESTful大多都采用JWT来做授权校验,在Spring Boot 中可以采用Shiro和JWT来做简单的权限以及认证验证,在和Spring Boot集成的过程中碰到了不少坑.便结合自身以 ...
- spring boot rest 接口集成 spring security(2) - JWT配置
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- Spring Boot HikariCP 一 ——集成多数据源
其实这里介绍的东西主要是参考的另外一篇文章,数据库读写分离的. 参考文章就把链接贴出来,里面有那位的代码,简单明了https://gitee.com/comven/dynamic-datasource ...
- spring boot rest 接口集成 spring security(1) - 最简配置
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- spring boot / cloud (三) 集成springfox-swagger2构建在线API文档
spring boot / cloud (三) 集成springfox-swagger2构建在线API文档 前言 不能同步更新API文档会有什么问题? 理想情况下,为所开发的服务编写接口文档,能提高与 ...
- Spring Boot系列——如何集成Log4j2
上篇<Spring Boot系列--日志配置>介绍了Spring Boot如何进行日志配置,日志系统用的是Spring Boot默认的LogBack. 事实上,除了使用默认的LogBack ...
- 【ELK】4.spring boot 2.X集成ES spring-data-ES 进行CRUD操作 完整版+kibana管理ES的index操作
spring boot 2.X集成ES 进行CRUD操作 完整版 内容包括: ============================================================ ...
- 15、Spring Boot 2.x 集成 Swagger UI
1.15.Spring Boot 2.x 集成 Swagger UI 完整源码: Spring-Boot-Demos 1.15.1 pom文件添加swagger包 <swagger2.versi ...
- 14、Spring Boot 2.x 集成 Druid 数据源
14.Spring Boot 2.x 集成 Druid 数据源 完整源码: Spring-Boot-Demos
- 12、Spring Boot 2.x 集成 MongoDB
1.12 Spring Boot 2.x 集成 MongoDB 完整源码: Spring-Boot-Demos
随机推荐
- python subprocess popen 静默模式(不弹出console控制台)
python subprocess popen 静默模式(不弹出console控制台) import subprocess,sys IS_WIN32 = 'win32' in str(sys.plat ...
- rac 关于RACScheduler的一点学习
RACScheduler 信号调度器,是一个线性执行队列,rac中的信号可以在RACScheduler上执行任务.发送结果,底层用GCD封装的. rac中提供生成线程的几个方法: 1:schedul ...
- Python3下UnicodeDecodeError:‘ASCII’ codec cant decode..(128)
今天准备用Keras跑一下LeNet的程序,结果总是编码出错 源代码是2.7写的,编码格式是utf-8.然后尝试网上各种方法不适用,最后还是解决了 源代码: data = gzip.open(r'C: ...
- hdfs冷热数据分层存储
hdfs如何让某些数据查询快,某些数据查询慢? hdfs冷热数据分层存储 本质: 不同路径制定不同的存储策略. hdfs存储策略 hdfs的存储策略 依赖于底层的存储介质. hdfs支持的存储介质: ...
- drf 第一节
drf django-restframework ''' 1.接口:接口的概念.数据接口文档.接口规范(restful).Postman接口测试工具 2.drf请求生命周期 - CBV 3.drf的基 ...
- spring-boot-configuration-processor 是干啥用的
spring默认使用yml中的配置,但有时候要用传统的xml或properties配置,就需要使用spring-boot-configuration-processor了 引入pom依赖 <de ...
- 【Android-自定义控件】 漂亮的Toast
修改Toast属性,美化Toast //创建一个Toast Toast toast=new Toast(getApplicationContext()); //创建Toast中的文字 TextView ...
- [引用]MATLAB中的fft后为何要用fftshift
原文地址:MATLAB中的fft后为何要用fftshift fft是一维傅里叶变换,即将时域信号转换为频域. fftshift是针对频域的,将FFT的DC分量移到频谱中心,重新排列fft,fft1和… ...
- linux如何查看ip地址
使用命令: ifconfig -a 例如:
- Linux之静态库
命名规则: lib + 库的名字 + .a 制作步骤 生成对应.o文件 .c à .o 将生成的.o文件打包 ar rcs + 静态库的名字(libMytest.a) + 生成的所有的.o 发布 ...