一个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. linux打开和关闭端口

    查看哪些端口被打开  netstat -anp 关闭端口号:iptables -A INPUT -p tcp -drop 8080 -j DROP 打开端口号:iptables -A INPUT -p ...

  2. 让Spring不再难懂-aop篇

    什么是aop AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.OOP允许 ...

  3. 【flask】RestFul的基本鉴权

    编写API的基本鉴权 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author : shenqiang from flask import Fla ...

  4. shared zone "SSL" has no equal addresses: 011F0000 vs 03460000

    在windows上配置nginx的https域名的安全证书时出现的问题. 搜索了一下,都说是因为 Nginx 缓存模块或其他使用共享缓存的模块不能在Windows Vista及以上的window上运行 ...

  5. php获取服务器和mysql等信息输出到页面(基于ci框架)

    function show($varName) {    switch($result = get_cfg_var($varName)) {     case 0:      return '< ...

  6. async包 ES6 async/await的区别

    最基本的async 包 ApCollection.find({}).toArray(function (err, aps) { var num = 0; async.whilst( function ...

  7. estt

    1.路由控制的定义 1.1.IP地址与路由控制 互联网是由路由器连接的网络组合而成的.为了能让数据包正确地到达目标主机,路由器必须在途中进行正确地转发.这种向"正确的方法"转发数据 ...

  8. 安装 Kali Linux 2018.1 及之后的事

    本文为原创文章,转载请标明出处 目录 制作U盘启动盘 安装 Kali Linux 之后的事 更新源 配置 Zsh 配置 Vim 修改 Firefox 语言为中文 安装 Gnome 扩展 美化 安装 G ...

  9. APP内计费规范出台 手游乱收费现象能被遏制?

    手游乱收费现象能被遏制?" title="APP内计费规范出台 手游乱收费现象能被遏制?"> 在一个混乱.无秩序的环境中竞争,虽然有可能不择手段地获取更多的利益,但 ...

  10. OPPO招聘-互联网测试

    邮       箱:ljy@oppo.com 工作地点:深圳