一个api要支持H5, PC和APP三个前端,如果使用session的话对app不是很友好,而且session有跨域攻击的问题,所以选择了JWT

1.导入依赖包

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency> <dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.2.0</version>
</dependency>

2.自定义JWTToken

import org.apache.shiro.authc.AuthenticationToken;

public class JwtToken implements AuthenticationToken {

    private String token;

    public JwtToken(String token) {
this.token = token;
} @Override
public Object getPrincipal() {
return token;
} @Override
public Object getCredentials() {
return token;
}
}

工具类

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT; import java.io.UnsupportedEncodingException;
import java.util.Date; public class JwtUtils { // 过期时间30天
private static final long EXPIRE_TIME = 24 * 60 * 30 * 1000; /**
* 校验token是否正确
*
* @param token 密钥
* @param username 登录名
* @param password 密码
* @return
*/
public static boolean verify(String token, String username, String password) {
try {
Algorithm algorithm = Algorithm.HMAC256(password); JWTVerifier verifier = JWT.require(algorithm).withClaim("userName", username).build(); DecodedJWT jwt = verifier.verify(token); return true;
} catch (Exception e) {
return false;
}
} /**
* 获取登录名
*
* @param token
* @return
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("userName").asString();
} catch (JWTDecodeException e) {
return null;
}
} /**
* 生成签名
*
* @param username
* @param password
* @return
*/
public static String sign(String username, String password) {
try {
// 指定过期时间
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); Algorithm algorithm = Algorithm.HMAC256(password); return JWT.create()
.withClaim("userName", username)
.withExpiresAt(date)
.sign(algorithm);
} catch (UnsupportedEncodingException e) {
return null;
}
} }

3.自定义realm

import com.system.authorization.model.JwtToken;
import com.system.authorization.model.MzUser;
import com.system.authorization.service.MzUserService;
import com.system.authorization.utils.JwtUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import java.util.Set; public class JwtShiroRealm extends AuthorizingRealm { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired
private MzUserService mzUserService; /**
* 使用JWT代替原生Token
* @param token
* @return
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
} //权限验证
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
logger.info("doGetAuthorizationInfo:" + principalCollection.toString()); String userName = JwtUtils.getUsername(principalCollection.toString()); //获取权限数据
Set<String> permissions = mzUserService.getPermissionByUserName(userName); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
} /**
* 身份认证:Authentication 用来验证用户身份
* 默认使用此方法进行用户名正确与否验证,错误抛出异常
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String token = authenticationToken.getPrincipal().toString(); System.out.println("Realm 验证:"+token);
String userName = JwtUtils.getUsername(token); System.out.println("Realm 验证用户名:"+userName);
MzUser mzUser = mzUserService.queryByUserName(userName);
if (mzUser == null) {
throw new AuthenticationException("token验证失败,权限不足");
} if (!JwtUtils.verify(token, userName, mzUser.getPassword())) {
throw new UnknownAccountException("token验证失败,权限不足");
} return new SimpleAuthenticationInfo(token, token, "realm");
}
}

4.自定义filter

import com.system.authorization.model.JwtToken;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; public class JwtAuthFilter extends BasicHttpAuthenticationFilter { private Logger logger = LoggerFactory.getLogger(this.getClass()); // 登录标识
private static String LOGIN_SIGN = "x-auth-token"; /**
* 检测用户是否登录
* 检测header里面是否包含Authorization字段即可
*
* @param request
* @param response
* @return
*/
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest httpRequest = WebUtils.toHttp(request); String authorization = httpRequest.getHeader(LOGIN_SIGN); return StringUtils.isNoneBlank(authorization);
} @Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpRequest = WebUtils.toHttp(request);
String token = httpRequest.getHeader(LOGIN_SIGN);
JwtToken jwtToken = new JwtToken(token);
//提交给realm进行登录,如果错误会怕熬出异常并被捕获,如果没有抛出异常则返回true
getSubject(request, response).login(jwtToken);
return true;
} @Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
System.out.println("开始jwt 校验");
//如果不是登录请求
if (isLoginAttempt(request, response)) {
try {
executeLogin(request, response);
} catch (Exception e) {
// throw new TSharkException("登录权限不足!", e);
throw new UnknownAccountException("token验证失败,权限不足");
}
}
System.out.println("jwt 校验通过");
return true;
} @Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
HttpServletResponse httpResponse = WebUtils.toHttp(response);
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json;charset=utf-8");
httpResponse.setStatus(org.apache.http.HttpStatus.SC_UNAUTHORIZED);
System.out.println("token验证失败,没权限访问");
return false;
} /**
* 对跨域提供支持
*
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
} }

授权过滤器

import org.apache.http.HttpStatus;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.util.WebUtils; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; public class RolesAndPermissionFilter extends AuthorizationFilter { @Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
System.out.println("开始Roles permission校验");
//获取接口请求地址
String path = WebUtils.toHttp(request).getRequestURI(); Subject subject = getSubject(request, response); //数据库中存储的是接口的请求地址,此处验证当前请求的接口地址,当前登录的用户是否存在,如果存在则通过验证
if (subject.isPermitted(path))
return true;
System.out.println("roles permission校验未通过");
return false;
} @Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
HttpServletResponse httpResponse = WebUtils.toHttp(response);
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json;charset=utf-8");
httpResponse.setStatus(HttpStatus.SC_UNAUTHORIZED);
return false;
}
}

5.配置信息,注入spring容器

import com.system.authorization.filter.JwtAuthFilter;
import com.system.authorization.filter.RolesAndPermissionFilter;
import com.system.authorization.realm.JwtShiroRealm;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
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.LinkedHashMap;
import java.util.Map; @Configuration
@ConditionalOnWebApplication
public class ShiroConfig { @Bean
public Realm jwtShiroRealm() {
return new JwtShiroRealm();
} @Bean
public SecurityManager securityManager() {
DefaultSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
defaultSecurityManager.setRealm(jwtShiroRealm()); // 关闭自带session
DefaultSessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator();
evaluator.setSessionStorageEnabled(false); DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
subjectDAO.setSessionStorageEvaluator(evaluator); defaultSecurityManager.setSubjectDAO(subjectDAO); return defaultSecurityManager;
} @Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); //将自定义的过滤器注入
Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put("jwt", new JwtAuthFilter());
filterMap.put("permission", new RolesAndPermissionFilter()); factoryBean.setFilters(filterMap);
factoryBean.setSecurityManager(securityManager); //定义过滤规则
Map<String, String> filterRuleMap = new HashMap<>();
//所有的请求都必须经过jwt,permission过滤器
filterRuleMap.put("/**", "jwt,permission");
//登录接口可以不做验证
filterRuleMap.put("/mz/user/login", "anon"); factoryBean.setFilterChainDefinitionMap(filterRuleMap); //设置登录页面,主页面,验证失败页面
factoryBean.setLoginUrl("https://www.baidu.com");
factoryBean.setSuccessUrl("https://www.cnblogs.com/gyli20170901/");
factoryBean.setUnauthorizedUrl("/403"); return factoryBean;
} @Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
} @Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
} @Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}

参考:https://yq.aliyun.com/articles/646440

spring boot Shiro JWT整合的更多相关文章

  1. spring boot shiro redis整合基于角色和权限的安全管理-Java编程

    一.概述 本博客主要讲解spring boot整合Apache的shiro框架,实现基于角色的安全访问控制或者基于权限的访问安全控制,其中还使用到分布式缓存redis进行用户认证信息的缓存,减少数据库 ...

  2. Spring Boot Security JWT 整合实现前后端分离认证示例

    前面两章节我们介绍了 Spring Boot Security 快速入门 和 Spring Boot JWT 快速入门,本章节使用 JWT 和 Spring Boot Security 构件一个前后端 ...

  3. Spring Boot认证:整合Jwt

    背景 Jwt全称是:json web token.它将用户信息加密到token里,服务器不保存任何用户信息.服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证. 优点 简洁: 可以通过 ...

  4. SpringBoot2.0+Shiro+JWT 整合

    SpringBoot2.0+Shiro+JWT 整合 JSON Web Token(JWT)是一个非常轻巧的规范.这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息. 我们利用一定的编 ...

  5. Spring Boot Shiro 使用教程

    Apache Shiro 已经大名鼎鼎,搞 Java 的没有不知道的,这类似于 .Net 中的身份验证 form 认证.跟 .net core 中的认证授权策略基本是一样的.当然都不知道也没有关系,因 ...

  6. Spring Boot 2.x整合Redis

    最近在学习Spring Boot 2.x整合Redis,在这里和大家分享一下,希望对大家有帮助. Redis是什么 Redis 是开源免费高性能的key-value数据库.有以下的优势(源于Redis ...

  7. spring boot 2.0 整合 elasticsearch6.5.3,spring boot 2.0 整合 elasticsearch NoNodeAvailableException

    原文地址:spring boot 2.0 整合 elasticsearch NoNodeAvailableException 原文说的有点问题,下面贴出我的配置: 原码云项目地址:https://gi ...

  8. Spring Boot入门 and Spring Boot与ActiveMQ整合

    1.Spring Boot入门 1.1什么是Spring Boot Spring 诞生时是 Java 企业版(Java Enterprise Edition,JEE,也称 J2EE)的轻量级代替品.无 ...

  9. Spring Boot和Dubbo整合

    provider端 POM依赖 <dependencies> <dependency> <groupId>org.springframework.boot</ ...

随机推荐

  1. JS创建数组的三种方法

    1     常规方式 创建数组并给数组元素赋值 var myCars = new Array(); myCars[0] = "Saab"; myCars[1] = "Vo ...

  2. Ubuntu 12.04 搭建TFTP服务器

    吐槽先:在Ubuntu上搭建TFTP服务器,网上搜到一堆资料,可惜基本都是部分能用,至于哪些部分能用还要自己摸索着试出来,郁闷之情仅次于找不到任何资料…… ---------------------- ...

  3. android五子棋游戏、资讯阅读、大学课程表、地图拖拽检测、小说搜索阅读app等源码

    Android精选源码 Android 自动生成添加控件 android旋转动画.圆形进度条组合效果源码 一款很强的手机五子棋app源码 android地图拖拽区域检测效果源码 实现Android大学 ...

  4. deeplearning.ai 卷积神经网络 Week 1 卷积神经网络

    1. 传统的边缘检测(比如Sobel)手工设计了3*3的filter(或者叫kernel)的9个权重,在深度学习中,这9个权重都是学习出来的参数,会比手工设计的filter更好,不但可以提取90度.0 ...

  5. Mybatis与Spring整合(纯注解)

    java1.5版本之后开始支持注解,spring*2开始提供注解配置方式,到spring**4后spring推荐使用注解配置 IOC注解(主要作用就是在spring容器中声明一个Bean,同xml中的 ...

  6. CentOS 5.5 --学习(1)

    一.解决浏览器乱码问题: 用yum 安装 yum  -y  install  fonts-chinese yum  -y  install  fonts-ISO8859-2-75dpiCentOS系统 ...

  7. spring security梳理

    核心服务:AuthenticationManager,UserDetailsService和AccessDecisionManager The AuthenticationManager, Provi ...

  8. Nginx笔记:对url中携带的参数进行多次判断

    Nginx中只支持简单的if语句,不支持多条件判断和嵌套,通过特殊的方式也可以达到效果 location / { proxy_set_header Host $host; proxy_set_head ...

  9. Redhat7 配置https

    Redhat7 配置https 分为自签名证书和第3方证书(此时实验为第3方,自签名略) 安装: # yum install httpd mod_ssl 生成key: # openssl genrsa ...

  10. 《内蒙古自治区第十二届大学生程序设计竞赛试题_D: 正品的概率》

    问题 D: 正品的概率 内存限制:128 MB时间限制:1 S标准输入输出 题目类型:传统评测方式:文本比较上传者:外部导入 提交:36通过:7 返回比赛提交提交记录 题目描述 袋中有m枚正品硬币,n ...