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的更多相关文章

  1. Spring Boot 最简单整合Shiro+JWT方式

    简介 目前RESTful大多都采用JWT来做授权校验,在Spring Boot 中可以采用Shiro和JWT来做简单的权限以及认证验证,在和Spring Boot集成的过程中碰到了不少坑.便结合自身以 ...

  2. spring boot rest 接口集成 spring security(2) - JWT配置

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  3. Spring Boot HikariCP 一 ——集成多数据源

    其实这里介绍的东西主要是参考的另外一篇文章,数据库读写分离的. 参考文章就把链接贴出来,里面有那位的代码,简单明了https://gitee.com/comven/dynamic-datasource ...

  4. spring boot rest 接口集成 spring security(1) - 最简配置

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  5. spring boot / cloud (三) 集成springfox-swagger2构建在线API文档

    spring boot / cloud (三) 集成springfox-swagger2构建在线API文档 前言 不能同步更新API文档会有什么问题? 理想情况下,为所开发的服务编写接口文档,能提高与 ...

  6. Spring Boot系列——如何集成Log4j2

    上篇<Spring Boot系列--日志配置>介绍了Spring Boot如何进行日志配置,日志系统用的是Spring Boot默认的LogBack. 事实上,除了使用默认的LogBack ...

  7. 【ELK】4.spring boot 2.X集成ES spring-data-ES 进行CRUD操作 完整版+kibana管理ES的index操作

    spring boot 2.X集成ES 进行CRUD操作  完整版 内容包括: ============================================================ ...

  8. 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 ...

  9. 14、Spring Boot 2.x 集成 Druid 数据源

    14.Spring Boot 2.x 集成 Druid 数据源 完整源码: Spring-Boot-Demos

  10. 12、Spring Boot 2.x 集成 MongoDB

    1.12 Spring Boot 2.x 集成 MongoDB 完整源码: Spring-Boot-Demos

随机推荐

  1. CSS世界中那些说起来很冷的知识

    CSS世界中那些说起来很冷的知识 最近读了张鑫旭的新书<CSS世界>收获了不少对CSS的深度理解 也正值个人在公司内部进行部分章节的内容分享,于是顺带着直接把我即将分享的内容先给大家过过目 ...

  2. Fiddler 显示客户端请求时间,请求耗时,服务器地址

    # 效果图 打开 CustomRules.js (C:\Users\UsersName\Documents\Fiddler2\Scripts):打开 fiddler 时 windows 快捷键 -&g ...

  3. 在Google Maps 上点击标签后显示说明

    JS如下: (function() {     window.onload = function() {           // Creating an object literal contain ...

  4. CodeForces 837D - Round Subset | Educational Codeforces Round 26

    /* CodeForces 837D - Round Subset [ DP ] | Educational Codeforces Round 26 题意: 选k个数相乘让末尾0最多 分析: 第i个数 ...

  5. 【SQL-历史执行语句查询】 查询对数据库执行了哪些SQL

    Sql语句 QS.creation_time as '创建时间', ), (( THEN DATALENGTH(st.text) ) ) AS '查询语句' , ST.text as '执行文本', ...

  6. 分布式协调框架_Zookeeper

    Zookeeper 如今在分布式架构中应用十分广泛,它作为分布式协调框架在分布式架构中有着举足轻重的地位,本文是主要从以上几个方面对 Zookeeper 常用的知识进行总结. 一 从集中式到分布式架构 ...

  7. hover([over,]out)

    hover([over,]out) 概述 一个模仿悬停事件(鼠标移动到一个对象上面及移出这个对象)的方法.这是一个自定义的方法,它为频繁使用的任务提供了一种“保持在其中”的状态. 当鼠标移动到一个匹配 ...

  8. contents() 查找匹配元素内部所有的子节点(包括文本节点)。如果元素是一个iframe,则查找文档内容

    contents() V1.2概述 查找匹配元素内部所有的子节点(包括文本节点).如果元素是一个iframe,则查找文档内容   示例 描述:大理石平台检定规程 查找所有文本节点并加粗 HTML 代码 ...

  9. luogu3629

    P3629 [APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通 ...

  10. CodeForces–830B--模拟,树状数组||线段树

    B. Cards Sorting time limit per test 1 second memory limit per test 256 megabytes input standard inp ...