本文介绍了Spring-boot结合Shrio实现JWT的方法,分享给大家,具体如下:
关于验证大致分为两个方面:
主要解决方法:使用自定义的Shiro Filter
项目搭建:
这是一个spring-boot 的web项目,不了解spring-boot的项目搭建,请google。
pom.mx引入相关jar包
<!-- shiro 权限管理 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version> </dependency> <!-- JWT --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency>
Shrio 的相关配置
划重点!!自定义了一个Filter
filterMap.put("JWTFilter", new JWTFilter());
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); // 添加自己的过滤器并且取名为JWTFilter Map<String, Filter> filterMap = new HashMap<>(); filterMap.put("JWTFilter", new JWTFilter()); shiroFilterFactoryBean.setFilters(filterMap); /* * 自定义url规则 * http://shiro.apache.org/web.html#urls- */ Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap(); filterChainDefinitionMap.put("/**", "JWTFilter"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * securityManager 不用直接注入shiroDBRealm,可能会导致事务失效 * 解决方法见 handleContextRefresh * http://www.debugrun.com/a/NKS9EJQ.html */ @Bean("securityManager") public DefaultWebSecurityManager securityManager(TokenRealm tokenRealm) { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(tokenRealm); /* * 关闭shiro自带的session,详情见文档 * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29 */ DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); defaultSessionStorageEvaluator.setSessionStorageEnabled(false); subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); manager.setSubjectDAO(subjectDAO); return manager; } @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean(name = "TokenRealm") @DependsOn("lifecycleBeanPostProcessor") public TokenRealm tokenRealm() { return new TokenRealm(); } @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); // 强制使用cglib,防止重复代理和可能引起代理出错的问题 // https://zhuanlan.zhihu.com/p/29161098 defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return new AuthorizationAttributeSourceAdvisor(); } }
自定义Shrio filter
执行顺序:preHandle -> doFilterInternal -> executeLogin -> onLoginSuccess
主要判断是不是登录请求的是 doFilterInternal
public class JWTFilter extends BasicHttpAuthenticationFilter { /** * 自定义执行登录的方法 */ @Override protected boolean executeLogin(ServletRequest request, ServletResponse response) throws IOException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; UsernamePasswordToken usernamePasswordToken = JSON.parseObject(httpServletRequest.getInputStream(), UsernamePasswordToken.class); // 提交给realm进行登入,如果错误他会抛出异常并被捕获 Subject subject = this.getSubject(request, response); subject.login(usernamePasswordToken); return this.onLoginSuccess(usernamePasswordToken, subject, request, response); //错误抛出异常 } /** * 最先执行的方法 */ @Override protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { return super.preHandle(request, response); } /** * 登录成功后登录的操作 * 加上jwt 的header */ @Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) { HttpServletResponse httpServletResponse = (HttpServletResponse) response; String jwtToken = Jwts.builder() .setId(token.getPrincipal().toString()) .setExpiration(DateTime.now().plusMinutes(30).toDate()) .signWith(SignatureAlgorithm.HS256, JWTCost.signatureKey) .compact(); httpServletResponse.addHeader(AUTHORIZATION_HEADER, jwtToken); return true; } /** * 登录以及校验的主要流程 * 判断是否是登录,或者是登陆后普通的一次请求 */ @Override public void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; String servletPath = httpServletRequest.getServletPath(); if (StringUtils.equals(servletPath, "/login")) { //执行登录 this.executeLogin(servletRequest, servletResponse); } else { String authenticationHeader = httpServletRequest.getHeader(AUTHORIZATION_HEADER); if (StringUtils.isNotEmpty(authenticationHeader)) { Claims body = Jwts.parser() .setSigningKey(JWTCost.signatureKey) .parseClaimsJws(authenticationHeader) .getBody(); if (body != null) { //更新token body.setExpiration(DateTime.now().plusMinutes(30).toDate()); String updateToken = Jwts.builder().setClaims(body).compact(); httpServletResponse.addHeader(AUTHORIZATION_HEADER, updateToken); //添加用户凭证 PrincipalCollection principals = new SimplePrincipalCollection(body.getId(), JWTCost.UserNamePasswordRealm);//拼装shiro用户信息 WebSubject.Builder builder = new WebSubject.Builder(servletRequest, servletResponse); builder.principals(principals); builder.authenticated(true); builder.sessionCreationEnabled(false); WebSubject subject = builder.buildWebSubject(); //塞入容器,统一调用 ThreadContext.bind(subject); filterChain.doFilter(httpServletRequest, httpServletResponse); } } else { httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value()); } } } }
登录失败处理
处理Shrio异常
@RestControllerAdvice public class GlobalControllerExceptionHandler { @ExceptionHandler(value = Exception.class) public Object allExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) { String message = exception.getCause().getMessage(); LogUtil.error(message); return new ResultInfo(exception.getClass().getName(), message); } /*=========== Shiro 异常拦截==============*/ @ExceptionHandler(value = IncorrectCredentialsException.class) public String IncorrectCredentialsException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "IncorrectCredentialsException"; } @ExceptionHandler(value = UnknownAccountException.class) public String UnknownAccountException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "UnknownAccountException"; } @ExceptionHandler(value = LockedAccountException.class) public String LockedAccountException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "LockedAccountException"; } @ExceptionHandler(value = ExcessiveAttemptsException.class) public String ExcessiveAttemptsException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "ExcessiveAttemptsException"; } @ExceptionHandler(value = AuthenticationException.class) public String AuthenticationException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "AuthenticationException"; } @ExceptionHandler(value = UnauthorizedException.class) public String UnauthorizedException(HttpServletRequest request, HttpServletResponse response, Exception exception) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "UnauthorizedException"; } }
处理JWT异常
这是个坑,因为是在filter内发生的异常,@ExceptionHandler是截获不到的。
/** * 截获spring boot Error页面 */ @RestController public class GlobalExceptionHandler implements ErrorController { @Override public String getErrorPath() { return "/error"; } @RequestMapping(value = "/error") public Object error(HttpServletRequest request, HttpServletResponse response) throws Exception { // 错误处理逻辑 Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception"); Throwable cause = exception.getCause(); if (cause instanceof ExpiredJwtException) { response.setStatus(HttpStatus.GATEWAY_TIMEOUT.value()); return new ResultInfo("ExpiredJwtException", cause.getMessage()); } if (cause instanceof MalformedJwtException) { response.setStatus(HttpStatus.FORBIDDEN.value()); return new ResultInfo("MalformedJwtException", cause.getMessage()); } return new ResultInfo(cause.getCause().getMessage(), cause.getMessage()); } }
关于权限等授权信息,可以直接放到Redis中实现缓存。我认为也是不错的。
源码奉上:githup-shiro分支 :温馨提示:平时测试代码可能比较乱。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍Spring Boot整合QueryDSL的实现示例,包括了Spring Boot整合QueryDSL的实现示例的使用技巧和注意事项,需要的朋友参考一下 之前研究Jooq,今天来研究一下搭配JPA的QueryDSL吧。 简介 Querydsl 是一个Java开源框架用于构建类型安全的SQL查询语句。它采用API代替拼凑字符串来构造查询语句。可跟 Hibernate 和 JPA 等框架
这是我启动应用程序时的控制台: 但是当我用邮递员访问时,我有一个错误:
我必须使用微服务架构来构建一个应用程序。我将整个系统分成多个组件,每个组件代表一个spring boot项目。6左右有几个spring boot项目。我已经使用eureka服务器注册了所有的服务以实现负载平衡。已经为web门户开发了一个单独的Spring Boot项目,该应用程序包含身份验证、Spring security授权和JWT协议。现在我有7个项目,包括一个web门户,每个项目都有控制器包
本文向大家介绍Spring Boot实现热部署的实例方法,包括了Spring Boot实现热部署的实例方法的使用技巧和注意事项,需要的朋友参考一下 Spring Boot怎么实现热部署 在Spring Boot实现代码热部署是一件很简单的事情,代码的修改可以自动部署并重新热启动项目。 1、引用devtools依赖 这样,当修改一个java类时就会热更新。 2、自定义配置热部署 以下配置用于自定义配
本文向大家介绍spring boot环境抽象的实现方法,包括了spring boot环境抽象的实现方法的使用技巧和注意事项,需要的朋友参考一下 在实际开发中,开发人员在编写springboot的时候通常要在本地环境测试然后再部署到Production环境,这两种环境一般来讲是不同的,最主要的区别就是数据源的不同。 在应用环境中,集成在容器的抽象环境模型有两个方面:profiles和properti
本文向大家介绍spring boot配置ssl实现HTTPS的方法,包括了spring boot配置ssl实现HTTPS的方法的使用技巧和注意事项,需要的朋友参考一下 传输层安全性协议(英语:Transport Layer Security,缩写作 TLS),及其前身安全套接层(Secure Sockets Layer,缩写作 SSL)是一种安全协议,目的是为互联网通信,提供安全及数据完整性保障。