一个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. reviewer回信

    收到reviewer回信之后的情况 Peer review其实是一个CA(质检)过程.文章投稿后的几种状态:Reject.resubmit和revise-and-resubmit. 收到回信之后,re ...

  2. The sequence and de novo assembly of the giant panda genome.ppt

    sequencing:使用二代测序原因:高通量,短序列 不用长序列原因: 1.算法错误率高 2.长序列测序将嵌合体基因错误积累.嵌合体基因:通过重组由来源与功能不同的基因序列剪接而形成的杂合基因 se ...

  3. linux 新添加的硬盘格式化并挂载到目录下方法

    需求: 新增加一块硬盘sdb,将sdb分区,只分一个区,格式化,挂载到目录/ssd下.原文:https://www.cnblogs.com/ddbear/p/7009736.html 1.  查看现在 ...

  4. Centos-Apache服务(2)

    title date tags layout CentOS6.5 Apache的增值服务 2018-09-03 Centos6.5服务器搭建 post 1.更改Apache的监听端口号 [root@l ...

  5. “pip install tensorflow ”出现错误

    在控制台命令窗口输入:pip install tensorflow之后出现一长串bug怎么解决 网上百度了一些方法: 安装Python3.5 安装Python3.6 总结原因:Python3.7没有合 ...

  6. Oralce获取32位随机数

    SELECT SYS_GUID() from dual;

  7. hashCode() 和 equals()比较

    1. 首先equals()和hashCode()这两个方法都是从Object类中继承过来的. equals()方法在Object类中定义如下: public boolean equals(Object ...

  8. [LC] 92. Reverse Linked List II

    Reverse a linked list from position m to n. Do it in one-pass. Note: 1 ≤ m ≤ n ≤ length of list. Exa ...

  9. [LC] 165. Compare Version Numbers

    Compare two version numbers version1 and version2.If version1 > version2 return 1; if version1 &l ...

  10. OpenCV 离散傅立叶变换

    #include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" #include ...