基于SpringBoot微服务的Spring Security身份认证机制研究
springsecurity认证机制的InvalidCSRFToken问题
springsecurity认证机制的InvalidCSRFToken问题
spring security在集成spring boot的微服务框架后,进⾏了cas认证和权限控制。
但是在请求过程中,发现了⼀个问题
1.关于错误
错误指出,请求中出现了不可⽤的CSRF令牌。
查阅资料后发现这是⼀个RESTful技术与CSRF(Cross-site request forgery跨站请求伪造)的冲突造成的,CSRF默认⽀持的⽅法:
GET|HEAD|TRACE|OPTIONS,不⽀持POST。
传统的session id容易被第三⽅窃取攻击,spring security4.0版本之后,引⼊了CSRF的概念。
spring security为了正确的区别合法的post请求,采⽤了token的机制。
过程⼤致为get请求会从服务器端拿到⼀个token,这个token被拿来当做header参数通过post请求传递⾄服务器。
服务器通过区分这个token值是否合法来判定是否是正常的post请求(⽽⾮第三⽅攻击)。
2.解决
spring Security 3默认关闭csrf,Spring Security 4默认启动了csrf。
开发环境⼿动关闭csrf
@Override
protected void configure(HttpSecurity http) throws Exception {
//访问控制内容。
http .csrf().disable();
}。
基于SpringBoot微服务架构下前后端分离的MVVM模型
基于SpringBoot微服务架构下前后端分离的MVVM模型一、概述随着信息技术的飞速发展和企业业务需求的不断变化,传统的单体应用架构已无法满足现代企业的需求。
微服务架构作为一种新型的分布式架构模式,通过将复杂的应用程序拆分成一组小的服务,每个服务运行在独立的进程中,并使用轻量级通信机制进行交互,从而提高了系统的可扩展性、可维护性和灵活性。
而SPringBoOt作为一个轻量级的JaVa框架,以其快速构建、易于部署和高度可配置的特点,成为了构建微服务架构的首选工具。
在微服务架构中,前后端分离是一种重要的设计原则。
通过将前端界面与后端业务逻辑分离,可以实现前后端的独立开发和部署,降低系统的耦合度,提高开发效率和用户体验。
前端负责处理用户界面和用户交互,后端则专注于提供数据和处理业务逻辑。
这种分离模式使得前后端可以分别采用最适合的技术栈和开发方法,从而充分发挥各自的优势。
MVVM(ModelViewViewModel)模型是一种前端架构设计模式,它在MVC(ModeiviewController)模式的基础上进行了改进,将视图(View)和控制器(Controller)的职责合并到ViewMOdeI中,实现了视图和模型之间的自动数据绑定。
在MVVM模型中,Model负责存储和管理数据,VieW负责展示用户界面,而VieWModel则作为MOdel和VieW之间的桥梁,负责将Model中的数据变化映射到VieW上,并处理用户的交互操作。
这种设计模式使得前端代码更加清晰、可维护,并且提高了用户体验。
本文将探讨在SpringBoot微服务架构下实现前后端分离的MVVM模型的方法和实践。
我们将介绍如何使用SpringBoot构建后端服务,并使用前端框架(如Vue.js)实现MVVM模型的前端界面。
通过具体的案例和实践经验,我们将展示如何在微服务架构下实现高效的前后端分离开发,提高系统的可扩展性、可维护性和用户体验。
springbootsecurity安全机制
springbootsecurity安全机制springboot security 安全机制认证流程:1、由认证配置WebSecurityConfigurerAdapter的configure(HttpSecurity http)⽅法进⼊,添加拦截器addFilterBefore2、进⼊拦截器AbstractAuthenticationProcessingFilter的attemptAuthentication⽅法,指定认证对象AbstractAuthenticationToken3、进⼊认证逻辑AuthenticationProvider,根据supports的断定对认证的⽬标对象指定对哪个拦截器进⾏认证,进⼊具体的认证逻辑⽅法authenticate4、认证结果:认证成功后进⼊拦截器的successfulAuthentication⽅法;认证失败后进⼊拦截器的unsuccessfulAuthentication⽅法5、对认证结果进⾏处理 a.认证成功的逻辑:进⼊SimpleUrlAuthenticationSuccessHandler的onAuthenticationSuccess⽅法 b.认证失败的逻辑:进⼊SimpleUrlAuthenticationFailureHandler的onAuthenticationFailure⽅法6、返回结果给页⾯,将数据封装在ObjectMapper对象中,将会以⽂本形式返回给客户端(页⾯)代码块⾃定义认证配置,实现WebSecurityConfigurerAdapterpackage com.travelsky.config;import com.travelsky.auto.login.BeforeLoginFilter;import com.travelsky.auto.login.LoginProvider;import com.travelsky.auto.login.OnFailureLogin;import com.travelsky.auto.token.OnFailureToken;import com.travelsky.auto.token.TokenFilter;import com.travelsky.auto.token.TokenProvider;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;import ernamePasswordAuthenticationFilter;/*** 认证配置*/@Configurationpublic class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {/*** 认证成功处理*/@Autowiredprivate SimpleUrlAuthenticationSuccessHandler successHandler;/*** 登录认证失败后处理*/@Autowiredprivate OnFailureLogin failureHandler;/*** token认证失败后处理*/@Autowiredprivate OnFailureToken onFailureToken;/*** 登录认证逻辑*/@Autowiredprivate LoginProvider loginProvider;/*** token认证逻辑*/@Autowiredprivate TokenProvider tokenProvider;/*** 配置请求权限* @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 允许登录路径通过 .antMatchers("/login").permitAll()// 允许业务路径通过 .antMatchers("/**").permitAll().and().authorizeRequests().anyRequest().authenticated().and().csrf().disable()// 添加拦截器 .addFilterBefore(getLoginFilter(), UsernamePasswordAuthenticationFilter.class).addFilterBefore(getTokenFilter(), UsernamePasswordAuthenticationFilter.class);}/*** 登录拦截器* @return* @throws Exception*/private AbstractAuthenticationProcessingFilter getLoginFilter() throws Exception {BeforeLoginFilter beforeLoginFilter = new BeforeLoginFilter("/login", successHandler, failureHandler);beforeLoginFilter.setAuthenticationManager(super.authenticationManager());return beforeLoginFilter;}/*** token拦截器* @return* @throws Exception*/private AbstractAuthenticationProcessingFilter getTokenFilter() throws Exception {TokenFilter tokenFilter = new TokenFilter("/**", onFailureToken);tokenFilter.setAuthenticationManager(super.authenticationManager());return tokenFilter;}/*** 配置认证逻辑列表,按顺序进⾏认证* @param auth*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) {auth.authenticationProvider(loginProvider);auth.authenticationProvider(tokenProvider);}}⾃定义拦截器,继承AbstractAuthenticationProcessingFilterpackage com.travelsky.auto.login;import lombok.extern.slf4j.Slf4j;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;import org.springframework.security.web.authentication.AuthenticationFailureHandler;import org.springframework.security.web.authentication.AuthenticationSuccessHandler;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** 登录⾃定义拦截器,继承AbstractAuthenticationProcessingFilter*/@Slf4jpublic class BeforeLoginFilter extends AbstractAuthenticationProcessingFilter {/***认证成功的处理对象*/private AuthenticationSuccessHandler successHandler;/*** 认证失败的处理对象*/private AuthenticationFailureHandler failureHandler;public BeforeLoginFilter(String defaultFilterProcessesUrl, AuthenticationSuccessHandler successHandler, AuthenticationFailureHandler failureHandler) {super(defaultFilterProcessesUrl);this.successHandler = successHandler;this.failureHandler = failureHandler;}/*** 过滤器处理逻辑* @param request* @param response* @return* @throws AuthenticationException* @throws IOException* @throws ServletException*/@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {("进⼊登录过滤器");String userName = request.getParameter("userName");String password = request.getParameter("password");LoginInfo loginInfo = new LoginInfo(null, userName, password);("username = {}", userName);("password = {}", password);// 返回指定的认证对象 LoginAuthenticationToken loginAuthentication = new LoginAuthenticationToken(null, loginInfo, null);// 将来由认证逻辑AuthenticationProvider来处理,这⾥返回LoginAuthenticationToken对象,将来由AuthenticationProvider的supports⽅法相匹配的认证逻辑来处理 return this.getAuthenticationManager().authenticate(loginAuthentication);}/*** 认证逻辑认证成功成功后会调⽤此⽅法* @param request* @param response* @param chain* @param authResult* @throws IOException* @throws ServletException*/@Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { successHandler.onAuthenticationSuccess(request, response, authResult);}/*** 认证逻辑认证失败后调⽤此⽅法* @param request* @param response* @param failed* @throws IOException* @throws ServletException*/@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {failureHandler.onAuthenticationFailure(request, response, failed);}}⾃定义认证逻辑,继承AuthenticationProviderpackage com.travelsky.auto.login;import com.alibaba.fastjson.JSONObject;import com.travelsky.pojo.MenuVO;import com.travelsky.pojo.SysUserInfo;import com.travelsky.service.SysUserInfoService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.authentication.AuthenticationProvider;import org.springframework.security.authentication.AuthenticationServiceException;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import ponent;import java.util.List;/*** 认证逻辑*/@Component@Slf4jpublic class LoginProvider implements AuthenticationProvider {@Autowiredprivate SysUserInfoService userInfoService;/*** 认证逻辑,认证的⽬标对象由supports断定,supports断定的⽬标对象是LoginAuthenticationToken⾃定义认证对象* @param authentication* @return* @throws AuthenticationException*/@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {("登录验证器,接收参数:{}", JSONObject.toJSONString(authentication));// Authentication的具体⼦类,可由supports断定,supports断定⼦类是LoginAuthenticationToken,因此可以强转 final LoginAuthenticationToken loginAuthentication = (LoginAuthenticationToken) authentication;final LoginInfo loginInfo = loginAuthentication.getLoginInfo();final String userName = loginInfo.getUserName();final SysUserInfo userInfo = userInfoService.getByUserId(userName);if (userInfo.getPassword().equals(loginInfo.getPassword())) {final List<MenuVO> userInfoMenus = userInfoService.getUserInfoMenus(userInfo.getId());return new LoginAuthenticationToken(null, loginInfo, userInfoMenus);} else {log.error("登录验证失败");throw new AuthenticationServiceException("登录失败.........");}}* 指定对谁进⾏认证,这⾥指定对LoginAuthenticationToken⾃定义认证对象进⾏认证* @param authentication* @return*/@Overridepublic boolean supports(Class<?> authentication) {return LoginAuthenticationToken.class.isAssignableFrom(authentication);}}⾃定义认证成功的处理逻辑,继承SimpleUrlAuthenticationSuccessHandlerpackage com.travelsky.auto.login;import com.alibaba.fastjson.JSONObject;import com.fasterxml.jackson.databind.ObjectMapper;import com.travelsky.auto.token.TokenContent;import com.travelsky.auto.token.TokenFactory;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.MediaType;import org.springframework.security.core.Authentication;import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;import ponent;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** ⾃定义认证成功处理逻辑,继承SimpleUrlAuthenticationSuccessHandler类*/@Component@Slf4jpublic class OnSuccessfulLogin extends SimpleUrlAuthenticationSuccessHandler {/*** 返回页⾯的对象,可将对象转化为⽂本形式(json格式)*/@Autowiredprivate ObjectMapper objectMapper;/*** token⼯⼚*/@Autowiredprivate TokenFactory tokenFactory;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { ("登录认证成功,传⼊参数:{}", JSONObject.toJSONString(authentication));// 配置响应编码格式为UTF-8 response.setCharacterEncoding("UTF-8");// 配置返回格式为jsonresponse.setContentType(MediaType.APPLICATION_JSON_VALUE);// Authentication的具体⼦类为LoginAuthenticationToken final LoginAuthenticationToken loginAuthenticationToken = (LoginAuthenticationToken) authentication;final TokenContent tokenContent = tokenFactory.createToken("token");JSONObject json = new JSONObject();json.put("code", "000000");json.put("message", "认证成功");json.put("claims", tokenContent.getClaims());json.put("token", tokenContent.getToken());json.put("menuList", loginAuthenticationToken.getUserInfoMenus());("⽣产token:{}",tokenContent.getToken());// 将数据保存到objectMapper中,将会转化为⽂本格式,返回给页⾯objectMapper.writeValue(response.getWriter(), json);}}⾃定义认证失败逻辑,继承SimpleUrlAuthenticationFailureHandlerpackage com.travelsky.auto.login;import com.alibaba.fastjson.JSONObject;import com.fasterxml.jackson.databind.ObjectMapper;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.MediaType;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;import ponent;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@Componentpublic class OnFailureLogin extends SimpleUrlAuthenticationFailureHandler {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {("登录认证失败,传⼊参数:{}", JSONObject.toJSONString(exception));response.setCharacterEncoding("UTF-8");response.setContentType(MediaType.APPLICATION_JSON_VALUE);JSONObject json = new JSONObject();json.put("code", "999999");json.put("message", exception.getMessage());objectMapper.writeValue(response.getWriter(), json);}}⾃定义认证对象,继承AbstractAuthenticationTokenpackage com.travelsky.auto.login;import com.travelsky.pojo.MenuVO;import org.springframework.security.authentication.AbstractAuthenticationToken;import org.springframework.security.core.GrantedAuthority;import java.util.Collection;import java.util.List;/*** ⾃定义登录认证对象*/public class LoginAuthenticationToken extends AbstractAuthenticationToken {/*** 登录数据对象*/private LoginInfo loginInfo;/*** 菜单数据对象*/private List<MenuVO> userInfoMenus;LoginAuthenticationToken(Collection<? extends GrantedAuthority> authorities, LoginInfo loginInfo, List<MenuVO> userInfoMenus) {super(authorities);erInfoMenus = userInfoMenus;this.loginInfo = loginInfo;}@Overridepublic Object getCredentials() {return loginInfo.getPassword();}@Overridepublic Object getPrincipal() {return loginInfo.getUserName();}public LoginInfo getLoginInfo() {return loginInfo;}public List<MenuVO> getUserInfoMenus() {return userInfoMenus;}}⾃定义拦截器:继承AbstractAuthenticationProcessingFilter,重写attemptAuthentication(HttpServletRequest request, HttpServletResponse response) ⽅法,返回AbstractAuthenticationToken的⼦类,AbstractAuthenticationToken的⼦类可⾃⼰定义内容(例如:⽤户信息,token字符串,Claims),只要继承AbstractAuthenticationToken即可⾃定义验证规则:实现AuthenticationProvider接⼝,authenticate⽅法参数Authentication与拦截器attemptAuthentication⽅法返回的是同⼀个对象,重写 authenticate(Authentication authentication),其中supports(Class<?> authentication)⽅法⽤来指定当前检验规则是针对哪个AbstractAuthenticationToken⼦类对象。
JavaSpringBoot框架在微服务架构中的应用研究
JavaSpringBoot框架在微服务架构中的应用研究一、引言随着互联网的快速发展,传统的单体应用已经无法满足当今复杂多变的业务需求。
微服务架构作为一种新兴的架构模式,逐渐成为了许多企业选择的方向。
而Java Spring Boot作为一款轻量级、快速开发的框架,也在微服务架构中扮演着重要的角色。
本文将探讨Java Spring Boot框架在微服务架构中的应用研究。
二、微服务架构概述微服务架构是一种以服务为中心构建软件系统的架构风格,将一个大型的应用程序拆分成一组小型、独立部署的服务。
每个服务都运行在自己的进程中,并通过轻量级通信机制进行通信。
微服务架构具有高内聚、松耦合、易于扩展等优点,能够更好地应对复杂多变的业务需求。
三、Java Spring Boot框架介绍Java Spring Boot是Spring家族中的一个项目,它简化了基于Spring框架的应用程序的开发过程。
Spring Boot提供了一套快速开发微服务的工具,包括自动化配置、快速部署等功能。
借助Spring Boot,开发者可以更加高效地开发出稳定、可靠的微服务应用。
四、Java Spring Boot在微服务架构中的优势快速开发:Spring Boot提供了丰富的starter依赖,可以快速搭建项目结构,减少开发人员的工作量。
自动化配置:Spring Boot通过约定大于配置的原则,能够自动配置大部分应用程序所需的组件,简化了配置过程。
监控管理:Spring Boot提供了丰富的监控管理功能,可以方便地监控应用程序的运行状态。
易于维护:微服务架构下,每个服务都是独立部署、独立维护的,利于团队协作和系统维护。
五、Java Spring Boot在微服务架构中的应用实践项目结构设计:在使用Spring Boot开发微服务时,需要合理设计项目结构,将不同功能模块拆分成独立的服务。
服务注册与发现:借助Eureka、Consul等注册中心,实现微服务之间的动态调用和负载均衡。
基于Spring Security的身份认证与授权
基于Spring Security的身份认证与授权随着互联网的不断发展,越来越多的网站和应用需要用户进行注册登录,进行身份验证之后才能进行访问和操作,这就提出了身份认证与授权的问题。
Spring Security是一个基于Spring框架的认证和授权的安全框架,它提供了全面的身份验证和授权管理的功能,能够简化开发者的开发工作。
一、Spring Security的作用Spring Security创建了一个安全环境,在这个环境中,程序员可以控制访问资源的安全性。
具体作用如下:1. 身份验证:Spring Security提供了多种验证方式,包括基于表单、基于HTTPS等方式,可以依据实际需要进行相应设置。
2. 授权管理:Spring Security提供了基于角色和权限的授权管理模型,可以实现精细的权限控制。
3. 攻击预防:Spring Security提供了攻击预防的功能,包括Csrf防护、Session管理等。
4. 安全事件:Spring Security提供了安全事件监听和处理机制,可以对安全事件进行处理和响应。
5. 记录和审核:Spring Security提供了记录和审核功能,可以对安全事件进行记录和审核。
二、Spring Security的配置Spring Security的配置分为两个部分:一是安全配置类,二是Spring配置文件。
1. 安全配置类:配置类需要继承WebSecurityConfigurerAdapter类,然后重写相应的方法。
@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {erDetailsService(userDetailsService);}@Overrideprotected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers(\"\/resources\/**\", \"\/signup\", \"\/about\").permitAll().antMatchers(\"\/admin\/**\").hasRole(\"ADMIN\").antMatchers(\"\/db\/**\").access(\"hasRole('ADMIN ') and hasRole('DBA')\").anyRequest().authenticated().and().formLogin().loginPage(\"\/login\").permitAll().and().logout().permitAll();}}2. Spring配置文件:<!-- 开启Spring Security --><security:http auto-config=\"true\" use-expressions=\"true\"> <security:intercept-url pattern=\"\/admin\/**\"access=\"hasRole('ROLE_ADMIN')\" \/><security:intercept-url pattern=\"\/**\"access=\"isAuthenticated()\" \/><security:form-login login-page=\"\/login\" \/><security:logout logout-success-url=\"\/\" \/><\/security:http><!-- 安全认证 --><security:authentication-manager><security:authentication-provider><security:user-service><security:user name=\"user\" password=\"user\" authorities=\"ROLE_USER\" \/><security:user name=\"admin\" password=\"admin\"authorities=\"ROLE_ADMIN\" \/><\/security:user-service><\/security:authentication-provider><\/security:authentication-manager>三、Spring Security的应用场景Spring Security可以应用于任何需要安全认证的项目中,比如以下几个场景:1. Web应用程序:Web应用程序通常需要用户通过表单登录进行身份验证,Spring Security可以很好地处理这些情况。
SpringBoot集成SpringSecurity用JWT令牌实现登录和鉴权的方法
SpringBoot集成SpringSecurity⽤JWT令牌实现登录和鉴权的⽅法最近在做项⽬的过程中需要⽤JWT做登录和鉴权查了很多资料都不甚详细有的是需要在application.yml⾥进⾏jwt的配置但我在导包后并没有相应的配置项因⽽并不适⽤在踩过很多坑之后稍微整理了⼀下做个笔记⼀、概念1、什么是JWTJson Web Token (JWT)是为了在⽹络应⽤环境间传递声明⽽执⾏的⼀种基于JSON的开放标准(RFC 7519)该token被设计为紧凑且安全的特别适⽤于分布式站点的单点登录(SSO)场景随着JWT的出现使得校验⽅式更加简单便捷化JWT实际上就是⼀个字符串它由三部分组成:头部载荷和签名⽤[.]分隔这三个部分最终的格式类似于:xxxx.xxxx.xxxx在服务器直接根据token取出保存的⽤户信息即可对token的可⽤性进⾏校验使得单点登录更为简单2、JWT校验的过程1、浏览器发送⽤户名和密码发起登录请求2、服务端验证⾝份根据算法将⽤户标识符打包⽣成token字符串并且返回给浏览器3、当浏览器需要发起请求时将token⼀起发送给服务器4、服务器发现数据中携带有token 随即进⾏解密和鉴权5、校验成功服务器返回请求的数据⼆、使⽤1、⾸先是导包<!-- Spring Security --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- Spring Security和JWT整合 --><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-jwt</artifactId><version>1.0.10.RELEASE</version></dependency><!-- JWT --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><!-- 字符串转换需要⽤到此包 --><dependency><groupId>mons</groupId><artifactId>commons-lang3</artifactId><version>3.4</version></dependency>2、实体类两个实体类⼀个是⽤户另⼀个是权限public class User {private Integer id;private String username;private String password;省略gettersetter之类的代码...}public class Role {private Integer id;private String username;private String name;省略gettersetter之类的代码...}3、然后需要⼀个Utils⼯具类该类⽤于进⾏Token的加密和解密可在此类中单元测试import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import java.util.Date;import java.util.HashMap;import java.util.Map;public class JwtTokenUtil {// Token请求头public static final String TOKEN_HEADER = "Authorization";// Token前缀public static final String TOKEN_PREFIX = "Bearer ";// 签名主题public static final String SUBJECT = "piconjo";// 过期时间public static final long EXPIRITION = 1000 * 24 * 60 * 60 * 7;// 应⽤密钥public static final String APPSECRET_KEY = "piconjo_secret";// ⾓⾊权限声明private static final String ROLE_CLAIMS = "role";/*** ⽣成Token*/public static String createToken(String username,String role) {Map<String,Object> map = new HashMap<>();map.put(ROLE_CLAIMS, role);String token = Jwts.builder().setSubject(username).setClaims(map).claim("username",username).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRITION)).signWith(SignatureAlgorithm.HS256, APPSECRET_KEY).compact();return token;}/*** 校验Token*/public static Claims checkJWT(String token) {try {final Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims;} catch (Exception e) {e.printStackTrace();return null;}}/*** 从Token中获取username*/public static String getUsername(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.get("username").toString();}/*** 从Token中获取⽤户⾓⾊*/public static String getUserRole(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.get("role").toString();}/*** 校验Token是否过期*/public static boolean isExpiration(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.getExpiration().before(new Date());}}4、配置UserDetailsService的实现类⽤于加载⽤户信息import xxx.xxx.xxx.bean.Role; // ⾃⼰的包import er; // ⾃⼰的包import erMapper; // ⾃⼰的包import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.authority.SimpleGrantedAuthority;import erDetails;import erDetailsService;import ernameNotFoundException;import org.springframework.stereotype.Service;import java.util.ArrayList;import java.util.List;@Servicepublic class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {if (s == null || "".equals(s)){throw new RuntimeException("⽤户不能为空");}// 调⽤⽅法查询⽤户User user = userMapper.findUserByUsername(s);if (user == null){throw new RuntimeException("⽤户不存在");}List<SimpleGrantedAuthority> authorities = new ArrayList<>();for (Role role:userMapper.findRoleByUsername(s)){authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));}return new er(user.getUsername(),"{noop}"+user.getPassword(),authorities); }}5、然后配置两个拦截器其中⼀个⽤于登录另⼀个⽤于鉴权JWTAuthenticationFilter登录拦截器:该拦截器⽤于获取⽤户登录的信息⾄于具体的验证只需创建⼀个token并调⽤authenticationManager的authenticate()⽅法让Spring security验证即可验证的事交给框架import com.alibaba.fastjson.JSON;import xxx.xxx.xxx.utils.JwtTokenUtil; // ⾃⼰的包import org.springframework.security.authentication.*;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.core.GrantedAuthority;import er;import ernamePasswordAuthenticationFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.Collection;/*** 验证⽤户名密码正确后⽣成⼀个token并将token返回给客户端*/public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {private AuthenticationManager authenticationManager;public JWTAuthenticationFilter(AuthenticationManager authenticationManager){this.authenticationManager = authenticationManager;}/*** 验证操作接收并解析⽤户凭证*/@Overridepublic Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {// 从输⼊流中获取到登录的信息// 创建⼀个token并调⽤authenticationManager.authenticate() 让Spring security进⾏验证return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getParameter("username"),request.getParameter("password")));}/*** 验证【成功】后调⽤的⽅法* 若验证成功⽣成token并返回*/@Overrideprotected void successfulAuthentication(HttpServletRequest request,HttpServletResponse response,FilterChain chain,Authentication authResult) throws IOException {User user= (User) authResult.getPrincipal();// 从User中获取权限信息Collection<? extends GrantedAuthority> authorities = user.getAuthorities();// 创建TokenString token = JwtTokenUtil.createToken(user.getUsername(), authorities.toString());// 设置编码防⽌乱码问题response.setCharacterEncoding("UTF-8");response.setContentType("application/json; charset=utf-8");// 在请求头⾥返回创建成功的token// 设置请求头为带有"Bearer "前缀的token字符串response.setHeader("token", JwtTokenUtil.TOKEN_PREFIX + token);// 处理编码⽅式防⽌中⽂乱码response.setContentType("text/json;charset=utf-8");// 将反馈塞到HttpServletResponse中返回给前台response.getWriter().write(JSON.toJSONString("登录成功"));}/*** 验证【失败】调⽤的⽅法*/@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { String returnData="";// 账号过期if (failed instanceof AccountExpiredException) {returnData="账号过期";}// 密码错误else if (failed instanceof BadCredentialsException) {returnData="密码错误";}// 密码过期else if (failed instanceof CredentialsExpiredException) {returnData="密码过期";}// 账号不可⽤else if (failed instanceof DisabledException) {returnData="账号不可⽤";}//账号锁定else if (failed instanceof LockedException) {returnData="账号锁定";}// ⽤户不存在else if (failed instanceof InternalAuthenticationServiceException) {returnData="⽤户不存在";}// 其他错误else{returnData="未知异常";}// 处理编码⽅式防⽌中⽂乱码response.setContentType("text/json;charset=utf-8");// 将反馈塞到HttpServletResponse中返回给前台response.getWriter().write(JSON.toJSONString(returnData));}}JWTAuthorizationFilter权限校验拦截器:当访问需要权限校验的URL(当然该URL也是需要经过配置的) 则会来到此拦截器在该拦截器中对传来的Token进⾏校验只需告诉Spring security该⽤户是否已登录并且是什么⾓⾊拥有什么权限即可import xxx.xxx.xxx.utils.JwtTokenUtil; // ⾃⼰的包import ng3.StringUtils;import org.springframework.security.authentication.AuthenticationManager;import ernamePasswordAuthenticationToken;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.ArrayList;import java.util.Collection;/*** 登录成功后⾛此类进⾏鉴权操作*/public class JWTAuthorizationFilter extends BasicAuthenticationFilter {public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {super(authenticationManager);}/*** 在过滤之前和之后执⾏的事件*/@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws IOException, ServletException {String tokenHeader = request.getHeader(JwtTokenUtil.TOKEN_HEADER);// 若请求头中没有Authorization信息或是Authorization不以Bearer开头则直接放⾏if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtil.TOKEN_PREFIX)){chain.doFilter(request, response);return;}// 若请求头中有token 则调⽤下⾯的⽅法进⾏解析并设置认证信息SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));super.doFilterInternal(request, response, chain);}/*** 从token中获取⽤户信息并新建⼀个token** @param tokenHeader 字符串形式的Token请求头* @return 带⽤户名和密码以及权限的Authentication*/private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) {// 去掉前缀获取Token字符串String token = tokenHeader.replace(JwtTokenUtil.TOKEN_PREFIX, "");// 从Token中解密获取⽤户名String username = JwtTokenUtil.getUsername(token);// 从Token中解密获取⽤户⾓⾊String role = JwtTokenUtil.getUserRole(token);// 将[ROLE_XXX,ROLE_YYY]格式的⾓⾊字符串转换为数组String[] roles = StringUtils.strip(role, "[]").split(", ");Collection<SimpleGrantedAuthority> authorities=new ArrayList<>();for (String s:roles){authorities.add(new SimpleGrantedAuthority(s));}if (username != null){return new UsernamePasswordAuthenticationToken(username, null,authorities);}return null;}}6、再配置⼀个⾃定义类⽤于进⾏匿名⽤户访问资源时⽆权限的处理该类需实现AuthenticationEntryPointimport com.alibaba.fastjson.JSONObject;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.AuthenticationEntryPoint;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException) throws IOException, ServletException {response.setCharacterEncoding("utf-8");response.setContentType("text/javascript;charset=utf-8");response.getWriter().print(JSONObject.toJSONString("您未登录,没有访问权限"));}}7、最后将这些组件组装到⼀起即可创建⼀个⾃定义的配置类继承WebSecurityConfigurerAdapter在该类上需加@EnableWebSecurity注解配置Web安全过滤器和启⽤全局认证机制import xxx.xxx.xxx.JWTAuthenticationEntryPoint; // ⾃⼰的包import xxx.xxx.xxx.xxx.JWTAuthenticationFilter; // ⾃⼰的包import xxx.xxx.xxx.xxx.JWTAuthorizationFilter; // ⾃⼰的包import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.context.annotation.Bean;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.config.http.SessionCreationPolicy;import erDetailsService;import org.springframework.web.cors.CorsConfiguration;import org.springframework.web.cors.CorsConfigurationSource;import org.springframework.web.cors.UrlBasedCorsConfigurationSource;@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowired@Qualifier("userDetailsServiceImpl")private UserDetailsService userDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {erDetailsService(userDetailsService);}/*** 安全配置*/@Overrideprotected void configure(HttpSecurity http) throws Exception {// 跨域共享http.cors().and()// 跨域伪造请求限制⽆效.csrf().disable().authorizeRequests()// 访问/data需要ADMIN⾓⾊.antMatchers("/data").hasRole("ADMIN")// 其余资源任何⼈都可访问.anyRequest().permitAll().and()// 添加JWT登录拦截器.addFilter(new JWTAuthenticationFilter(authenticationManager()))// 添加JWT鉴权拦截器.addFilter(new JWTAuthorizationFilter(authenticationManager())).sessionManagement()// 设置Session的创建策略为:Spring Security永不创建HttpSession 不使⽤HttpSession来获取SecurityContext .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()// 异常处理.exceptionHandling()// 匿名⽤户访问⽆权限资源时的异常.authenticationEntryPoint(new JWTAuthenticationEntryPoint());}/*** 跨域配置* @return 基于URL的跨域配置信息*/@BeanCorsConfigurationSource corsConfigurationSource() {final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();// 注册跨域配置source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());return source;}}定义⼀个⽤于测试的对外映射接⼝:@RestControllerpublic class UserController {@GetMapping("/data")private ResponseUtil data(){return "This is data.";}}默认登录路径是/login⽤POST请求发送若要修改默认的登录路径只需要在⾃⼰定义的登录过滤器JWTAuthenticationFilter的构造⽅法⾥进⾏配置即可⽐如若想修改为/api/login:public JWTAuthenticationFilter(AuthenticationManager authenticationManager){this.authenticationManager = authenticationManager;// 设置登录URLsuper.setFilterProcessesUrl("/api/login");}登录时参数的属性名分别是username和password 不能改动:登录成功后会返回⼀个Token:在请求需要权限的接⼝路径时若不带上Token 则会提⽰没有访问权限带上Token后再次请求即可正常访问:注:Token的前⾯要带有Bearer的前缀这样⼀个基本的实现就差不多完成了为简单演⽰在该案例中就不对密码进⾏加密了实际开发是需要对明⽂密码加密后存储的推荐⽤BCrypt进⾏加密和解密为节省篇幅⽤于注册的接⼝也不写了实际上在注册接⼝传⼊的密码也需要⽤BCrypt加密后再存⼊数据库中还可以⽤Redis进⾏Token的存储这些都是后话了到此这篇关于SpringBoot集成Spring Security⽤JWT令牌实现登录和鉴权的⽅法的⽂章就介绍到这了,更多相关SpringBoot JWT令牌登录和鉴权内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。
使用Spring Security进行用户认证和数据安全
使用Spring Security进行用户认证和数据安全随着互联网的发展,存储和传输的数据日益增多,处理和保护这些数据的能力成为软件系统设计中的一个重要问题。
在实际应用中,用户认证和数据安全成为了设计中的一项必备功能,而使用Spring Security是一种非常好的做法。
首先,什么是Spring Security?Spring Security是一个基于Spring框架的安全框架,它提供了一套完善的安全管理解决方案。
主要功能包括用户认证和授权、密码加密、安全事件监听和审计、Web安全和API安全等。
Spring Security可以帮助我们轻松地实现各种安全策略,并集成到我们的代码中。
接下来,我们来看一下如何使用Spring Security进行用户认证和数据安全。
1. 用户认证用户认证是指根据用户提供的账户信息来确认用户身份的过程,通常需要输入用户名和密码。
Spring Security提供了多种认证方式,包括基于数据库、LDAP、内存等多种方式。
下面以基于内存的认证方式为例,演示如何使用Spring Security完成用户认证。
首先,在Spring Boot的配置文件中添加如下配置:```=adminer.password=adminer.roles=ADMIN```这段配置代码中,我们定义了一个用户名为admin,密码为admin,角色为ADMIN的用户。
Spring Security会根据这个用户的信息进行认证。
接下来,在Spring Boot的启动类中添加如下注解:```@EnableWebSecuritypublic class MyApplication extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated() //请求认证.and().formLogin(); //使用默认的表单登录页面}@Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication() //使用内存认证.withUser("admin").password("{noop}admin").roles("ADM IN"); //定义用户}}```这段代码中,我们首先使用@EnableWebSecurity注解开启Web 安全功能。
Springboot+SpringSecurity+JWT实现用户登录和权限认证示例
Springboot+SpringSecurity+JWT实现⽤户登录和权限认证⽰例如今,互联⽹项⽬对于安全的要求越来越严格,这就是对后端开发提出了更多的要求,⽬前⽐较成熟的⼏种⼤家⽐较熟悉的模式,像RBAC 基于⾓⾊权限的验证,shiro框架专门⽤于处理权限⽅⾯的,另⼀个⽐较流⾏的后端框架是Spring-Security,该框架提供了⼀整套⽐较成熟,也很完整的机制⽤于处理各类场景下的可以基于权限,资源路径,以及授权⽅⾯的解决⽅案,部分模块⽀持定制化,⽽且在和oauth2.0进⾏了很好的⽆缝连接,在移动互联⽹的授权认证⽅⾯有很强的优势,具体的使⽤⼤家可以结合⾃⼰的业务场景进⾏选取和使⽤下⾯来说说关于单点登录中⽬前⽐较流⾏的⼀种使⽤⽅式,就是springsecurity+jwt实现⽆状态下⽤户登录;JWT在之前的篇章中⼤致提到过,使⽤jwt在分布式项⽬中进⾏⽤户信息的认证很⽅便,各个模块只需要知道配置的秘钥,就可以解密token中⽤户的基本信息,完成认证,很⽅便,关于使⽤jwt的基本内容可以查阅相关资料,或者参考我之前的⼀篇;整理⼀下思路1、搭建springboot⼯程2、导⼊springSecurity跟jwt的依赖3、⽤户的实体类,dao层,service层(真正开发时再写,这⾥就直接调⽤dao层操作数据库)4、实现UserDetailsService接⼝5、实现UserDetails接⼝6、验证⽤户登录信息的拦截器7、验证⽤户权限的拦截器8、springSecurity配置9、认证的Controller以及测试的controller项⽬结构pom⽂件<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- https:///artifact/org.springframework.security.oauth/spring-security-oauth2 --><dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>2.3.5.RELEASE</version></dependency><!-- https:///artifact/org.springframework.security/spring-security-jwt --><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-jwt</artifactId><version>1.0.10.RELEASE</version></dependency><!-- https:///artifact/org.springframework.security.oauth.boot/spring-security-oauth2-autoconfigure --><dependency><groupId>org.springframework.security.oauth.boot</groupId><artifactId>spring-security-oauth2-autoconfigure</artifactId><version>2.1.4.RELEASE</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- mybatis依赖 --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- redis依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency></dependencies>application.propertiesserver.port=8091#数据库连接spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true spring.datasource.driver-class-name=com.mysql.jdbc.Driverername=rootspring.datasource.password=root#mybatis配置mybatis.type-aliases-package=com.congge.entitymybatis.mapper-locations=classpath:mybatis/*.xml#redis配置spring.session.store-type=redisspring.redis.database=0spring,redis.host=127.0.0.1spring.redis.port=6379spring.redis.pool.min-idle=10000spring.redis.timeout=30000为模拟⽤户登录,这⾥提前创建了⼀个测试使⽤的表,user实体类Userpublic class User {private Integer id;private String username;private String password;private String role;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {ername = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getRole() {return role;}public void setRole(String role) {this.role = role;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", role='" + role + '\'' +'}';}}Jwt⼯具类,⽤于管理token相关的操作,可以单测使⽤public class TestJwtUtils {public static final String TOKEN_HEADER = "Authorization";public static final String TOKEN_PREFIX = "Bearer ";public static final String SUBJECT = "congge";public static final long EXPIRITION = 1000 * 24 * 60 * 60 * 7;public static final String APPSECRET_KEY = "congge_secret";private static final String ROLE_CLAIMS = "rol";public static String generateJsonWebToken(Users user) {if (user.getId() == null || user.getUserName() == null || user.getFaceImage() == null) {return null;}Map<String,Object> map = new HashMap<>();map.put(ROLE_CLAIMS, "rol");String token = Jwts.builder().setSubject(SUBJECT).setClaims(map).claim("id", user.getId()).claim("name", user.getUserName()).claim("img", user.getFaceImage()).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRITION)).signWith(SignatureAlgorithm.HS256, APPSECRET_KEY).compact();return token;}/*** ⽣成token* @param username* @param role* @return*/public static String createToken(String username,String role) {Map<String,Object> map = new HashMap<>();map.put(ROLE_CLAIMS, role);String token = Jwts.builder().setSubject(username).setClaims(map).claim("username",username).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRITION)).signWith(SignatureAlgorithm.HS256, APPSECRET_KEY).compact();return token;}public static Claims checkJWT(String token) {try {final Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody(); return claims;} catch (Exception e) {e.printStackTrace();return null;}}/*** 获取⽤户名* @param token* @return*/public static String getUsername(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.get("username").toString();}/*** 获取⽤户⾓⾊* @param token* @return*/public static String getUserRole(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.get("rol").toString();}/*** 是否过期* @param token* @return*/public static boolean isExpiration(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.getExpiration().before(new Date());}public static void main(String[] args) {String name = "acong";String role = "rol";String token = createToken(name,role);System.out.println(token);Claims claims = checkJWT(token);System.out.println(claims.get("username"));System.out.println(getUsername(token));System.out.println(getUserRole(token));System.out.println(isExpiration(token));}/*** eyJhbGciOiJIUzI1NiJ9.* eyJzdWIiOiJjb25nZ2UiLCJpZCI6IjExMDExIiwibmFtZSI6Im51b3dlaXNpa2kiLCJpbWciOiJ3d3cudW9rby5jb20vMS5wbmciLCJpYXQiOjE1NTQ5OTI1NzksImV4cCI6MTU1NTU5NzM3OX0. * 6DJ9En-UBcTiMRldZeevJq3e1NxJgOWryUyim4_-tEE** @param args*//*public static void main(String[] args) {Users user = new Users();user.setId("11011");user.setUserName("nuoweisiki");user.setFaceImage("/1.png");String token = generateJsonWebToken(user);System.out.println(token);Claims claims = checkJWT(token);if (claims != null) {String id = claims.get("id").toString();String name = claims.get("name").toString();String img = claims.get("img").toString();String rol = claims.get("rol").toString();System.out.println("id:" + id);System.out.println("name:" + name);System.out.println("img:" + img);System.out.println("rol:" + rol);}}*/}操作数据库的类,这⾥主要是提供⽤户注册的⼀个save⽤户的⽅法,@Servicepublic class UserService {@Autowiredprivate UserDao userDao;public void save(User user) {user.setId(1);userDao.save(user);}}JwtUser该类封装登录⽤户相关信息,例如⽤户名,密码,权限集合等,需要实现UserDetails 接⼝,public class JwtUser implements UserDetails {private Integer id;private String username;private String password;private Collection<? extends GrantedAuthority> authorities;public JwtUser() {}// 写⼀个能直接使⽤user创建jwtUser的构造器public JwtUser(User user) {id = user.getId();username = user.getUsername();password = user.getPassword();authorities = Collections.singleton(new SimpleGrantedAuthority(user.getRole()));}public Collection<? extends GrantedAuthority> getAuthorities() {return authorities;}public String getPassword() {return password;}public String getUsername() {return username;}public boolean isAccountNonExpired() {return true;}public boolean isAccountNonLocked() {return true;}public boolean isCredentialsNonExpired() {return true;}public boolean isEnabled() {return true;}@Overridepublic String toString() {return "JwtUser{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", authorities=" + authorities +'}';}}配置拦截器JWTAuthenticationFilterJWTAuthenticationFilter继承于UsernamePasswordAuthenticationFilter该拦截器⽤于获取⽤户登录的信息,只需创建⼀个token并调⽤authenticationManager.authenticate()让spring-security去进⾏验证就可以了,不⽤⾃⼰查数据库再对⽐密码了,这⼀步交给spring去操作。
SpringSecurity安全框架解析
SpringSecurity安全框架解析随着互联网的快速发展,保护用户数据和系统安全变得尤为重要。
SpringSecurity作为一个强大的安全框架,为开发人员提供了一种简单而可靠的方式来保护应用程序。
本文将对SpringSecurity进行详细分析,以便更好地理解和应用该框架。
一、SpringSecurity简介SpringSecurity是一个开源的基于Java的框架,它专注于提供身份验证(Authentication)和授权(Authorization)机制,以及处理常见的Web安全问题。
它与Spring框架无缝集成,并为开发人员提供了丰富而灵活的配置选项。
SpringSecurity可以用于保护Web应用程序、REST服务和方法级别的安全。
二、SpringSecurity的核心概念1. 身份验证(Authentication)身份验证是SpringSecurity的核心功能之一。
它负责验证用户的身份,并对其进行认证。
SpringSecurity支持多种认证方式,包括基本认证(Basic Authentication)、表单认证(Form-based Authentication)和OAuth2认证等。
通过配置合适的认证提供者和过滤器,可以实现灵活的身份验证需求。
2. 授权(Authorization)授权是SpringSecurity的另一个核心功能。
它负责确定经过身份验证的用户是否具有特定的权限来访问受保护的资源。
SpringSecurity提供了一种声明式的授权方式,即通过配置角色和权限来实现精确的访问控制。
开发人员可以根据实际业务需要进行灵活的授权配置。
3. 过滤器链(Filter Chain)过滤器链是SpringSecurity的关键概念之一。
它由一系列的过滤器组成,按照顺序依次对请求进行处理。
过滤器链可以根据具体的安全需求进行定制,例如添加验证码过滤器、记住我功能的过滤器等。
springsecurity认证流程
springsecurity认证流程Spring Security是一个基于Spring框架的安全框架,提供了一套完整的认证(Authentication)和授权(Authorization)解决方案。
它能够帮助我们轻松地实现用户认证、用户授权以及其他安全相关的功能。
Spring Security的认证流程可以分为以下几个步骤:1.用户发送登录请求:用户在浏览器中输入用户名和密码,并点击登录按钮。
这个请求会发送到服务器端。
2. 过滤器链处理请求:请求到达服务器端后,会经过一系列的过滤器链进行处理。
Spring Security提供了一些过滤器,用于处理认证和授权相关的任务。
3. 调用AuthenticationManager.authenticate(方法:在过滤器链的处理过程中,最终会调用AuthenticationManager.authenticate(方法来进行身份认证。
AuthenticationManager是Spring Security用于处理身份认证的核心接口。
4. 调用AuthenticationProvider进行身份认证:AuthenticationManager会委托给一个或多个AuthenticationProvider 来进行身份认证。
AuthenticationProvider是Spring Security中用于处理身份认证的实际实现类,可以根据自己的需求选择适合的实现类,例如DaoAuthenticationProvider。
5. 加载用户信息:AuthenticationProvider在进行身份认证之前,首先需要加载用户的信息。
具体的加载方式可以根据实际需求来选择,例如从数据库中加载用户信息。
6. 对比密码:AuthenticationProvider在加载用户信息后,会将输入的密码与数据库中存储的密码进行对比。
如果密码匹配成功,则认证通过。
7. 返回Authentication对象:如果身份认证通过,AuthenticationProvider会构建一个包含认证信息的Authentication对象,并返回给AuthenticationManager。
SpringSecurity框架原理解析
SpringSecurity框架原理解析Spring Security框架原理解析Spring Security是一种基于Spring框架的开源项目,用于保护企业应用程序的安全性。
它提供了一种全面的认证和授权方法,旨在解决企业级应用程序中的各种安全问题。
本文将深入探讨Spring Security框架的原理和工作机制。
一、概述Spring Security的核心任务是为企业级应用程序提供认证和授权机制。
它通过与Spring框架的集成,为应用程序提供了灵活、可扩展和安全的访问控制解决方案。
Spring Security的工作原理基于一系列的过滤器链,它们按特定顺序处理来自客户端的请求,并采取相应的安全措施。
二、认证机制1. 用户认证Spring Security提供了多种用户认证的方式,包括基于表单、基于HTTP基本认证、基于令牌等。
具体认证方式的选择取决于应用程序的需求。
2. 用户凭证验证凭证验证是确保用户身份的有效性。
Spring Security支持多种凭证验证方式,如用户名密码验证、LDAP验证、数据库验证等。
凭证验证的结果将决定用户是否被授予权限。
3. 认证流程Spring Security的认证流程包括过滤器链的处理、用户凭证的验证和认证成功或失败的处理。
认证成功后,用户信息将被存储在安全上下文中,以供后续的访问控制和权限管理使用。
三、授权机制1. 权限管理Spring Security提供了强大的权限管理机制,可基于角色或资源进行权限控制。
通过配置访问规则和授权策略,开发人员可以实现灵活细粒度的权限管理。
2. 访问控制访问控制是Spring Security的核心功能之一,它确保只有被授权的用户可以访问受限资源。
通过配置访问决策器和访问决策管理器,开发人员可以根据业务需求定义细致的访问控制规则。
四、安全漏洞防范Spring Security框架内置了多种安全防范措施,帮助开发人员有效防范常见的安全漏洞。
SpringBoot集成SpringsecurityJWT实现接口权限认证
SpringBoot集成SpringsecurityJWT实现接⼝权限认证1、添加依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>2、集成JWT⼯具类(JwtUtils)package com.dreamteam.chdapp.utils;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.servlet.http.HttpServletRequest;import java.util.Date;import java.util.HashMap;import java.util.Map;/*** @Author HeYunHui* @create 2020/11/15 14:12*/public class JwtUtils {private static final Logger logger= LoggerFactory.getLogger(JwtUtils.class);public static final long EXPIRATION_TIME=60*60*1000;// 令牌环有效期public static final String SECRET="abc123456def";//令牌环密钥public static final String TOKEN_PREFIX="Bearer";//令牌环头标识public static final String HEADER_STRING="Passport";//配置令牌环在http heads中的键值public static final String ROLE="ROLE";//⾃定义字段-⾓⾊字段//⽣成令牌环public static String generateToken(String userRole,String userid){HashMap<String,Object> map=new HashMap<>();map.put(ROLE,userRole);map.put("userid",userid);String jwt= Jwts.builder().setClaims(map).setExpiration(new Date(System.currentTimeMillis()+EXPIRATION_TIME)).signWith(SignatureAlgorithm.HS512,SECRET).compact();return TOKEN_PREFIX+" "+jwt;}//⽣成令牌环public static String generateToken(String userRole,String userid,long exprationtime){HashMap<String,Object> map=new HashMap<>();map.put(ROLE,userRole);map.put("userid",userid);String jwt= Jwts.builder().setClaims(map).setExpiration(new Date(System.currentTimeMillis()+exprationtime)).signWith(SignatureAlgorithm.HS512,SECRET).compact();return TOKEN_PREFIX+" "+jwt;}//令牌环校验public static Map<String,Object> validateTokenAndGetClaims(HttpServletRequest request){String token=request.getHeader(HEADER_STRING);if(token==null){throw new TokenValidationException("Missing Token");}else{Map<String,Object> body= Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token.replace(TOKEN_PREFIX,"")).getBody();return body;}}static class TokenValidationException extends RuntimeException{public TokenValidationException(String msg){super(msg);}}}3、集成JWT filter(拦截器/过滤器)package com.dreamteam.chdapp.filter;import com.dreamteam.chdapp.utils.JwtUtils;import ernamePasswordAuthenticationToken;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.util.AntPathMatcher;import org.springframework.util.PathMatcher;import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.Arrays;import java.util.Map;import static com.dreamteam.chdapp.utils.JwtUtils.ROLE;/*** @Author HeYunHui* @create 2020/11/15 14:46*/public class JwtAuthenticationFilter extends OncePerRequestFilter {private static final PathMatcher pathmatcher = new AntPathMatcher();private String[] protectUrlPattern = {"/manage/**", "/member/**", "/auth/**"}; //哪些请求需要进⾏安全校验public JwtAuthenticationFilter() {}@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { //是不是可以在这⾥做多种⽅式登录呢try {if (isProtectedUrl(httpServletRequest)) {Map<String, Object> claims = JwtUtils.validateTokenAndGetClaims(httpServletRequest);String role = String.valueOf(claims.get(ROLE));String userid = String.valueOf(claims.get("userid"));//最关键的部分就是这⾥, 我们直接注⼊了SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userid, null, Arrays.asList(() -> role)));}} catch (Exception e) {e.printStackTrace();httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());return;}filterChain.doFilter(httpServletRequest, httpServletResponse);}//是否是保护连接private boolean isProtectedUrl(HttpServletRequest request) {boolean flag = false;for (int i = 0; i < protectUrlPattern.length; i++) {if (pathmatcher.match(protectUrlPattern[i], request.getServletPath())) {return true;}}return false;}}4、配置JWT config类(配置类)跨域访问:客户端与服务端域名不同或是端⼝号不同。
SpringBoot第五篇SpringSecurity认证机制
SpringBoot第五篇SpringSecurity认证机制⼀、SpringSecurity主要⽤于鉴权和授权功能,他的过滤器链路如图1. BasicAuthenticationFilter实现的是HttpBasic模式的登录认证2. UsernamePasswordAuthenticationFilter实现⽤户名密码的登录认证3. RememberMeAuthenticationFilter实现登录认证的“记住我”的功能4. SmsCodeAuthenticationFilter实现短信验证码登录认证5. SocialAuthenticationFilter实现社交媒体⽅式登录认证的处理6. Oauth2AuthenticationProcessingFilter和Oauth2ClientAuthenticationProcessingFilter实现Oauth2的鉴权⽅式⼀、登录认证,调⽤⽅法ip/login,⾃动触发UsernamePasswordAuthenticationFilter过滤器 ⾃定义过滤器,继承该过滤器,可以实现验证之前的操作,⽐如验证码判断1.重写验证⽅法public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)throws AuthenticationException {try {String username = req.getParameter("username");String password = req.getParameter("password");//验证码判断,⼿机验证码判断等// 将⽤户信息放⼊authenticationManagerreturn authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username,password,Collections.emptyList()));} catch (Exception e) {throw new RuntimeException(e);}} 2、验证成功⽅法重写,可以返回token等数据protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res,FilterChain chain, Authentication auth) throws IOException, ServletException {//返回处理的token} 3、。
springsecurity整合springboot实现简单认证授权
springsecurity整合springboot实现简单认证授权主要⽤到了springboot,springsecurity,mybatis,jsp1. 创建项⽬2. 数据库表字段信息3. 创建⾓⾊ pojo对象这⾥直接使⽤SpringSecurity的⾓⾊规范4. 创建⽤户pojoimport java.util.Collection;import java.util.List;/*** @author john* @date 2020/1/11 - 20:15*/public class SysUser implements UserDetails {private Integer id;private String username;private String password;private Integer status;private List<SysRole> roles = new ArrayList<>();public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public void setUsername(String username) {ername = username;}public void setPassword(String password) {this.password = password;}public Integer getStatus() {return status;}public void setStatus(Integer status) {this.status = status;}public List<SysRole> getRoles() {return roles;}public void setRoles(List<SysRole> roles) {this.roles = roles;}@JsonIgnore@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() { return roles;}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {6. 编写RoleMapper8. 编写UserService以及其实现类10. 编写启动类11. 编写⼀个控制器⽤于访问12. 配置异常拦截器13. 测试jsp等静态页⾯资源查看码云代码SpringBoot官⽅是不推荐在SpringBoot中使⽤jsp的,需要导⼊tomcat插件启动项⽬,不能再⽤SpringBoot默认tomcat了在maven中执⾏输⼊⽤户名john密码123登录成功当访问权限不⾜的页⾯会显⽰⾃定义页⾯正常访问其他页⾯14. 代码地址15. 知识点备忘1. Spring Security主要jar包功能介绍2. Spring Security 常⽤过滤器介绍1. org.springframework.security.web.context.SecurityContextPersistenceFilter⾸当其冲的⼀个过滤器,作⽤之重要,⾃不必多⾔。
Springboot之security框架登录安全验证授权流程
Springboot之security框架登录安全验证授权流程⼀、只进⾏⽤户验证,不验证密码(UserDetailsService) 1、导⼊security依赖(在前端html页⾯进⾏⼀些判断需导⼊xmlns:sec="/thymeleaf-extras-springsecurity4")1<dependency>2<groupId>org.springframework.boot</groupId>3<artifactId>spring-boot-starter-security</artifactId>4</dependency>56<dependency>7<groupId>org.thymeleaf.extras</groupId>8<artifactId>thymeleaf-extras-springsecurity5</artifactId>9<version>3.0.4.RELEASE</version>10</dependency> 2、配置安全认证类1import org.springframework.beans.factory.annotation.Autowired;2import org.springframework.context.annotation.Bean;3import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;4import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;5import org.springframework.security.config.annotation.web.builders.HttpSecurity;6import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;7import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;8import erDetailsService;9import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;10import org.springframework.security.crypto.password.PasswordEncoder;11import top.xiaoyl.service.CustomUserDetailService;1213 @EnableWebSecurity14 @EnableGlobalMethodSecurity(prePostEnabled = true)15public class MySecurityConfig extends WebSecurityConfigurerAdapter {1617 @Override18protected void configure(HttpSecurity http) throws Exception {19//super.configure(http);20//定制请求的授权规则21 http.authorizeRequests().antMatchers("/admin/**","/js/**","/css/**","/images/*","/fonts/**","/**/*.png","/**/*.jpg").permitAll()22 .antMatchers("/","/index.html").permitAll()23 .antMatchers("/report").hasRole("USER")24 .and()25 .csrf().ignoringAntMatchers("/druid/*");262728/*29 * 开启⾃动配置的登陆功能,效果,如果没有登陆,没有权限就会来到登陆页⾯30 * 1、login来到登陆页31 * 2、重定向到/login?error表⽰登陆失败32 * 3、更多详细规定33 * 4、默认post形式的 /login代表处理登陆34 * 5、⼀但定制loginPage;那么 loginPage的post请求就是登陆35*/36 http.formLogin().usernameParameter("username").passwordParameter("password")37 .loginPage("/login");383940/*41 * 开启⾃动配置的注销功能42 * 1、访问 /logout 表⽰⽤户注销,清空session43 * 注销成功会返回 /login?logout 页⾯;44 * 注销成功以后来到⾸页45*/46 http.logout().logoutSuccessUrl("/");474849/*50 * 开启记住我功能51 * 登陆成功以后,将cookie发给浏览器保存,以后访问页⾯带上这个cookie,只要通过检查就可以免登录52 * 点击注销会删除cookie53*/54 http.rememberMe().rememberMeParameter("remember");57 }5859 @Bean60 UserDetailsService customUserDetailService() { // 注册UserDetailsService 的bean61 return new CustomUserDetailService();62 }6364/**65 * 认证信息管理66 * spring5中摒弃了原有的密码存储格式,官⽅把spring security的密码存储格式改了67 *68*/69 @Autowired70public void configure(AuthenticationManagerBuilder auth) throws Exception {71 erDetailsService(customUserDetailService()) //认证信息存储(⾃定义)72 .passwordEncoder(passwordEncoder());73//74// auth.inMemoryAuthentication() //认证信息存储到内存中75// .passwordEncoder(passwordEncoder())76// .withUser("user").password(passwordEncoder().encode("123")).roles("USER");7778 }7980private PasswordEncoder passwordEncoder() {81return new BCryptPasswordEncoder();82 }8384 } 3、⾃定义⽤户验证类实现UserDetailsService接⼝,进⾏授权验证1 @Service2public class CustomUserDetailService implements UserDetailsService {34 @Autowired5private UserService userService;67 @Autowired8 HttpServletRequest request;910 @Override11public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {12 HttpSession session = request.getSession();13 User user = userService.getUserByUsername(username);14if(user==null){15 session.setAttribute("loginMsg","⽤户名"+username+"不存在");16throw new UsernameNotFoundException("⽤户名"+username+"不存在");17 }18 List<SimpleGrantedAuthority> authorities = new ArrayList<>();19 authorities.add(new SimpleGrantedAuthority("USER"));20return new er(user.getUsername(),user.getPassword(),authorities);21 }22 }⼆、进⾏⽤户密码验证(AbstractUserDetailsAuthenticationProvider) 第⼀种⽅法虽然也能认证授权,但是问题显⽽易见,没有进⾏密码验证,主要⽤户名对了就给权限这很危险 流程跟⼀基本⼀样 1、导⼊依赖 2、配置安全认证类,需要报红⾊的地⽅修改⼀下1 @Bean2 AbstractUserDetailsAuthenticationProvider customAuthenticationProvide() { // 注册AbstractUserDetailsAuthenticationProvider的bean3return new CustomAuthenticationProvider();4 }56 @Autowired7public void configure(AuthenticationManagerBuilder auth) throws Exception {8 auth.authenticationProvider(customAuthenticationProvide()); //认证信息存储(⾃定义)9//10// auth.inMemoryAuthentication() //认证信息存储到内存中11// .passwordEncoder(passwordEncoder())12// .withUser("user").password(passwordEncoder().encode("123")).roles("USER");1314 } 3、⾃定义⽤户验证类实现AbstractUserDetailsAuthenticationProvider类,进⾏授权验证1import org.springframework.beans.factory.annotation.Autowired;2import org.springframework.security.authentication.BadCredentialsException;3import ernamePasswordAuthenticationToken;4import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;5import org.springframework.security.core.AuthenticationException;6import org.springframework.security.core.authority.SimpleGrantedAuthority;7import erDetails;8import ernameNotFoundException;9import org.springframework.stereotype.Service;10import er;1112import javax.servlet.http.HttpServletRequest;13import javax.servlet.http.HttpSession;14import java.util.ArrayList;15import java.util.List;1617 @Service18public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {1920 @Autowired21private UserService userService;2223 @Autowired24private HttpServletRequest request;2526 @Override27protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { 2829 }3031 @Override32protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {33 System.out.println("retrieveUser");34 HttpSession session = request.getSession();35if(username==null || username.length()==0){36 session.setAttribute("loginMsg","⽤户名不能为空");37throw new UsernameNotFoundException("⽤户名不能为空");38 }39 User user = userService.getUserByUsername(username);40if(user==null){41 session.setAttribute("loginMsg","⽤户名"+username+"不存在");42throw new UsernameNotFoundException("⽤户名"+username+"不存在");43 }44if(!authentication.getCredentials().toString().equals(user.getPassword())){45 session.setAttribute("loginMsg","密码不正确");46throw new BadCredentialsException("密码不正确");47 }48 List<SimpleGrantedAuthority> authorities = new ArrayList<>();49 authorities.add(new SimpleGrantedAuthority("USER"));50return new er(user.getUsername(),user.getPassword(),authorities);51 }52 }。
Springboot集成SpringSecurity实现JWT认证的步骤详解
Springboot集成SpringSecurity实现JWT认证的步骤详解⽬录1 简介2 项⽬整合2.1 JWT整合2.1.1 JWT⼯具类2.1.2 Token处理的Filter2.1.3 JWT属性2.2 Spring Security整合2.2.1 WebSecurityConfigurerAdapter配置2.2.2 ⽤户从哪来3 测试4 总结1 简介Spring Security作为成熟且强⼤的安全框架,得到许多⼤⼚的青睐。
⽽作为前后端分离的SSO⽅案,JWT也在许多项⽬中应⽤。
本⽂将介绍如何通过Spring Security实现JWT认证。
⽤户与服务器交互⼤概如下:1. 客户端获取JWT,⼀般通过POST⽅法把⽤户名/密码传给server;2. 服务端接收到客户端的请求后,会检验⽤户名/密码是否正确,如果正确则⽣成JWT并返回;不正确则返回错误;3. 客户端拿到JWT后,在有效期内都可以通过JWT来访问资源了,⼀般把JWT放在请求头;⼀次获取,多次使⽤;4. 服务端校验JWT是否合法,合法则允许客户端正常访问,不合法则返回401。
2 项⽬整合我们把要整合的Spring Security和JWT加⼊到项⽬的依赖中去:<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>2.1 JWT整合2.1.1 JWT⼯具类JWT⼯具类起码要具有以下功能:根据⽤户信息⽣成JWT;校验JWT是否合法,如是否被篡改、是否过期等;从JWT中解析⽤户信息,如⽤户名、权限等;具体代码如下:@Componentpublic class JwtTokenProvider {@Autowired JwtProperties jwtProperties;@Autowiredprivate CustomUserDetailsService userDetailsService;private String secretKey;@PostConstructprotected void init() {secretKey = Base64.getEncoder().encodeToString(jwtProperties.getSecretKey().getBytes());}public String createToken(String username, List<String> roles) {Claims claims = Jwts.claims().setSubject(username);claims.put("roles", roles);Date now = new Date();Date validity = new Date(now.getTime() + jwtProperties.getValidityInMs());return Jwts.builder()//.setClaims(claims)//.setIssuedAt(now)//.setExpiration(validity)//.signWith(SignatureAlgorithm.HS256, secretKey)//.compact();}public Authentication getAuthentication(String token) {UserDetails userDetails = erDetailsService.loadUserByUsername(getUsername(token));return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());}public String getUsername(String token) {return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();}public String resolveToken(HttpServletRequest req) {String bearerToken = req.getHeader("Authorization");if (bearerToken != null && bearerToken.startsWith("Bearer ")) {return bearerToken.substring(7);}return null;}public boolean validateToken(String token) {try {Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);if (claims.getBody().getExpiration().before(new Date())) {}return true;} catch (JwtException | IllegalArgumentException e) {throw new InvalidJwtAuthenticationException("Expired or invalid JWT token");}}}⼯具类还实现了另⼀个功能:从HTTP请求头中获取JWT。
springboot+springSecurity+jwt实现用户访问认证和授权
springboot+springSecurity+jwt实现⽤户访问认证和授权本⽂讲述的是springboot集成springSecurity和JWT的实现。
前后端分离⽬前已成为互联⽹项⽬开发的业界标准,其核⼼思想就是前端(APP、⼩程序、H5页⾯等)通过调⽤后端的API接⼝,提交及返回JSON数据进⾏交互。
在前后端分离项⽬中,⾸先要解决的就是登录及授权的问题。
传统的session认证限制了应⽤的扩展能⼒,⽆状态的JWT认证⽅法应运⽽⽣,该认证机制特别适⽤于分布式站点的单点登录(SSO)场景。
⼀,导⼊SpringSecurity与JWT的相关依赖<!--Security框架--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- jwt --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.10.6</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.10.6</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.10.6</version></dependency>⼆,定义SpringSecurity需要的基础处理类 application-dev.properties 加⼊jwt配置信息 ##jwt# 令牌keyjwt.header = Authorization# 令牌前缀jwt.token-start-with = Bearer# 使⽤Base64对该令牌进⾏编码jwt.base64-secret = U2FsdGVkX1/3Ox76xzrqllLe1lIgoHycDTgwVYrFQTPhG9V1lQPnLerFS/tmN1PzrQmx5243Nu9/iJf88neqOA==# 令牌过期时间此处单位/毫秒jwt.token-validity-in-seconds = 14400000 创建⼀个jwt的配置类,并注⼊Spring,便于程序中调⽤import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Configuration;/*** @ProjectName: git-dev* @Package: com.lq.pys.base.config* @ClassName: JwtSecurityProperties* @Author: xxx* @Description: JWT配置类* @Date: 2021/2/18 10:55 上午*/@Data@Configuration@ConfigurationProperties(prefix = "jwt")public class JwtSecurityProperties {/** Request Headers : Authorization */private String header;/** 令牌前缀,最后留个空格 Bearer */private String tokenStartWith;/** Base64对该令牌进⾏编码 */private String base64Secret;/** 令牌过期时间此处单位/毫秒 */private Long tokenValidityInSeconds;/**返回令牌前缀 */public String getTokenStartWith() {return tokenStartWith + " ";}} 定义⽆权限访问类import com.fasterxml.jackson.databind.ObjectMapper;import com.lq.pys.base.core.BDic;import com.lq.pys.base.core.BaseOut;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.web.access.AccessDeniedHandler;import ponent;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** @ProjectName: git-dev* @Package: mon* @ClassName: JwtAccessDeniedHandler* @Author: xxx* @Description: jwt⽆权限访问类* @Date: 2021/2/18 11:28 上午*/@Componentpublic class JwtAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {// 这个是⾃定义的返回对象,看各⾃需求BaseOut baseOut = new BaseOut();baseOut.setCode(BDic.FAIL);baseOut.setMessage("⽆权限查看此页⾯,请联系管理员!");baseOut.setTimestamp(Long.valueOf(System.currentTimeMillis()).toString());response.setContentType("application/json");response.setStatus(HttpServletResponse.SC_OK);try {ObjectMapper mapper = new ObjectMapper();mapper.writeValue(response.getOutputStream(), baseOut);} catch (Exception e) {throw new ServletException();}}} 定义认证失败处理类import com.fasterxml.jackson.databind.ObjectMapper;import com.lq.pys.base.core.BDic;import com.lq.pys.base.core.BaseOut;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.AuthenticationEntryPoint;import ponent;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** @ProjectName: git-dev* @Package: mon* @ClassName: JwtAuthenticationEntryPoint* @Author: xxx* @Description: JWT认证失败处理类* @Date: 2021/2/18 11:31 上午*/@Componentpublic class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request,HttpServletResponse response,AuthenticationException authException) throws IOException, ServletException {BaseOut baseOut = new BaseOut();baseOut.setCode(BDic.FAIL);baseOut.setMessage("⽆权限查看此页⾯,请联系管理员");baseOut.setTimestamp(Long.valueOf(System.currentTimeMillis()).toString());response.setContentType("application/json");response.setStatus(HttpServletResponse.SC_OK);try {ObjectMapper mapper = new ObjectMapper();mapper.writeValue(response.getOutputStream(), baseOut);} catch (Exception e) {throw new ServletException();}}}三,构建JWT token⼯具类 ⼯具类实现创建token与校验token功能import com.lq.pys.base.config.JwtSecurityProperties;import erInfo;import io.jsonwebtoken.*;import io.jsonwebtoken.io.Decoders;import io.jsonwebtoken.security.Keys;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.InitializingBean;import ernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import ponent;import java.security.Key;import java.util.*;import java.util.stream.Collectors;/*** @ProjectName: git-dev* @Package: com.lq.pys.util* @ClassName: JwtTokenUtils* @Author: xxx* @Description: JWT* @Date: 2021/2/18 11:01 上午*/@Slf4j@Componentpublic class JwtTokenUtils implements InitializingBean {private final JwtSecurityProperties jwtSecurityProperties;private static final String AUTHORITIES_KEY = "auth";private Key key;public JwtTokenUtils(JwtSecurityProperties jwtSecurityProperties) {this.jwtSecurityProperties = jwtSecurityProperties;}@Overridepublic void afterPropertiesSet() {byte[] keyBytes = Decoders.BASE64.decode(jwtSecurityProperties.getBase64Secret());this.key = Keys.hmacShaKeyFor(keyBytes);}public String createToken (Map<String, Object> claims) {return Jwts.builder().claim(AUTHORITIES_KEY, claims).setId(UUID.randomUUID().toString()).setIssuedAt(new Date()).setExpiration(new Date((new Date()).getTime() + jwtSecurityProperties.getTokenValidityInSeconds())) .compressWith(CompressionCodecs.DEFLATE).signWith(key, SignatureAlgorithm.HS512).compact();}public Date getExpirationDateFromToken(String token) {Date expiration;try {final Claims claims = getClaimsFromToken(token);expiration = claims.getExpiration();} catch (Exception e) {expiration = null;}return expiration;}public Authentication getAuthentication(String token) {Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();Collection<? extends GrantedAuthority> authorities =Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")).map(SimpleGrantedAuthority::new).collect(Collectors.toList());HashMap map =(HashMap) claims.get("auth");UserInfo principal = new UserInfo(map);return new UsernamePasswordAuthenticationToken(principal, token, authorities);}public boolean validateToken(String authToken) {try {Jwts.parser().setSigningKey(key).parseClaimsJws(authToken);return true;} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {log.error("token失效",e);} catch (ExpiredJwtException e) {log.error("token过期",e);} catch (UnsupportedJwtException e) {log.error("⽆效的token",e);} catch (IllegalArgumentException e) {log.error("处理token异常.",e);}return false;}private Claims getClaimsFromToken(String token) {Claims claims;try {claims = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();} catch (Exception e) {claims = null;}return claims;}}四,实现token验证的过滤器 该类继承OncePerRequestFilter,它能够确保在⼀次请求中只通过⼀次filter。
SpringBoot+SpringSecurity基本使用及个性化登录配置详解
SpringBoot+SpringSecurity基本使⽤及个性化登录配置详解Spring Security 基本介绍这⾥就不对Spring Security进⾏过多的介绍了,具体的可以参考我就只说下SpringSecurity核⼼功能:1. 认证(你是谁)2. 授权(你能⼲什么)3. 攻击防护(防⽌伪造⾝份)基本环境搭建这⾥我们以SpringBoot作为项⽬的基本框架,我这⾥使⽤的是maven的⽅式来进⾏的包管理,所以这⾥先给出集成Spring Security的⽅式<dependencies>...<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>...</dependencies>然后建⽴⼀个Web层请求接⼝@RestController@RequestMapping("/user")public class UserController {@GetMappingpublic String getUsers() {return "Hello Spring Security";}}接下来可以直接进⾏项⽬的运⾏,并进⾏接⼝的调⽤看看效果了。
通过⽹页的调⽤但是我们是没法正常访问的,出现了下图的⾝份验证输⼊框这是因为在SpringBoot中,默认的Spring Security就是⽣效了的,此时的接⼝都是被保护的,我们需要通过验证才能正常的访问。
Spring Security提供了⼀个默认的⽤户,⽤户名是user,⽽密码则是启动项⽬的时候⾃动⽣成的。
我们查看项⽬启动的⽇志,会发现如下的⼀段LogUsing default security password: 62ccf9ca-9fbe-4993-8566-8468cc33c28c当然你看到的password肯定和我是不⼀样的,我们直接⽤user和启动⽇志中的密码进⾏登录。
SpringBoot访问安全之认证和鉴权详解
SpringBoot访问安全之认证和鉴权详解⽬录拦截器认证鉴权在web应⽤中有⼤量场景需要对⽤户进⾏安全校,⼀般⼈的做法就是硬编码的⽅式直接埋到到业务代码中,但可曾想过这样做法会导致代码不够简洁(⼤量重复代码)、有个性化时难维护(每个业务逻辑访问控制策略都不相同甚⾄差异很⼤)、容易发⽣安全泄露(有些业务可能不需要当前登录信息,但被访问的数据可能是敏感数据由于遗忘⽽没有受到保护)。
为了更安全、更⽅便的进⾏访问安全控制,我们可以想到的就是使⽤springmvc的拦截器(HandlerInterceptor),但其实更推荐使⽤更为成熟的spring security来完成认证和鉴权。
拦截器拦截器HandlerInterceptor确实可以帮我们完成登录拦截、或是权限校验、或是防重复提交等需求。
其实基于它也可以实现基于url 或⽅法级的安全控制。
如果你对spring mvc的请求处理流程相对的了解,它的原理容易理解。
public interface HandlerInterceptor {/*** Intercept the execution of a handler. Called after HandlerMapping determined* an appropriate handler object, but before HandlerAdapter invokes the handler.** 在业务处理器处理请求之前被调⽤。
预处理,可以进⾏编码、安全控制、权限校验等处理** handler:controller内的⽅法,可以通过HandlerMethod method= ((HandlerMethod)handler);获取到@RequestMapping*/boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;/*** Intercept the execution of a handler. Called after HandlerAdapter actually* invoked the handler, but before the DispatcherServlet renders the view.** 在业务处理器处理请求执⾏完成后,⽣成视图之前执⾏。
springboot+jwt+springSecurity微信小程序授权登录问题
springboot+jwt+springSecurity微信⼩程序授权登录问题⽬录微信⼩程序授权登录问题思路总的来说的⽤户认证关键点总结场景重现:1.微信⼩程序向后台发送请求 ——⽽后台web采⽤的springSecuriry没有token⽣成,就会拦截请求,,所以⼩编记录下这个问题微信⼩程序授权登录问题思路参考⽹上⼀⼤堆资料核⼼关键字:⾃定义授权+鉴权(说的通俗就是解决办法就是改造springSecurity的过滤器)参考⽂章总的来说的通过⾃定义的WxAppletAuthenticationFilter替换默认的UsernamePasswordAuthenticationFilter,在UsernamePasswordAuthenticationFilter中可任意定制⾃⼰的登录⽅式。
springSecurity的原来的登录过滤器UsernamePasswordAuthenticationFilter采⽤账户+密码的形式说明我微信⼩程序这⾥很有可能不适⽤要升级,因为微信⼩程序采⽤openid的形式登录,⽽没有password⽤户认证需要结合JWT来实现⽤户认证,第⼀步登录成功后如何颁发token。
关键点使⽤cn.hutool.http请求第三⽅数据<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>4.5.16</version></dependency>说明:请求第三⽅数据时,需要授权。
第三⽅(微信⼩程序)会给到appid和secret,请求携带appid和secret获取⼀个token和expires,⼜了token就⼜了操作第三⽅数据的权限。
每次操作第三⽅数据时就需要携带token。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
2019.081概述身份认证是企业级项目开发的核心功能,其通过某种业务逻辑对用户输入的认证信息进行核准,认定其合法性,确保核心资源安全性与稳定性,如何实现身份认证是企业级应用的重要考核指标。
Spring Security 是一个能够为基于Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。
其提供了一组可以在Spring 应用上下文中配置的Bean 及相关的配置文档,借助IOC (控制反转),DI (依赖注入)和AOP (面向切面编程)功能将用户身份认证进行统一的管理,从而实现对资源访问的综合管控与维护[1]。
但是,Spring+Spring Security 框架组合存在着代码量大、配置复杂、紧耦合等问题,造成项目开发周期长、逻辑结构实现困难、维护工作量大[2]。
SpringBoot+Spring Security 微服务技术体系对代码量进行了大幅缩减,配置结构逻辑更加清晰,同时删除了大量的配置操作,简化了开发流程[3],结合Spring Da⁃ta JPA 持久化层框架将开发速度进一步提高,使用BCryptPasswordEncoder 进行密码加密,提出了为自定义密码加密算法方法,为企业级用户身份认证提供了一个优秀的开发框架[4]。
以建立在SpringBoot +Spring Security +SpringMVC +Thymeleaf+Spring Data JPA 综合框架上的某Web 应用平台的身份认证系统为背景,针对于满足企业级的用户身份认证的问题,结合企业实际的工作流程,探讨如何利用SpringBoot +Spring Security 微服务技术体系构建用户身份认证流程,实现低代码量、零配置、松耦合的企业级安全认证框架开发[5]。
2核心技术及框架介绍2.1SpringBoot 框架SpringBoot 是Pivotal 团队开发的基于Spring 框架的致力于简化项目开发流程、优化项目代码结构、实现零配置的微服务开发框架体系,其较传统的Spring 框架在代码书写形式、业务逻辑实现、项目部署实施及松耦合项目开发等方面均有大幅度飞跃。
SpringBoot +RestController 又在SOA (面向服务架构)领域开发提供了一套高效的技术架构,简单快捷的满足不同系统间的信息交互。
2.2Spring Security 框架Spring Security 是一个能够为基于Spring 的企业应用系统提供声明式的安全访问控制解决方式的安全框架,应用的安全性包括Authentication (认证)和Au⁃thorization (授权)两个部分。
Spring Security 的前身是基金项目:广东高校省级重点平台和重大科研项目特色创新类项目(自然科学)(2017GKTSCX027);广东省高职教育信息技术类专业教学指导委员会教育教学改革项目(XXJZW2018025);广东科贸职业学院科研项目(GDKM2018-26)。
作者简介:王悦(1979-),女,通讯作者,高级工程师,CCF 会员,研究方向:融媒体技术、新媒体技术;张雷(1978-),男,计算机科学与技术实验师,硕士,CCF 会员,研究方向:微服务框架、分布式计算;钱英军(1971-),男,教授,硕士,研究方向:工业控制、现场总线、软件工程、人工智能。
收稿日期:2019-05-10基于SpringBoot 微服务的Spring Security 身份认证机制研究王悦*,张雷,钱英军(广东科贸职业学院,广州510640)摘要:针对于Spring+Spring Security 框架组合在用户身份认证管理方面存在代码量大、配置复杂、紧耦合等问题,提出了使用SpringBoot+Spring Security 微服务技术在用户身份认证管理方面的安全架构设计与开发的整体解决方案。
设计了基于SpringBoot 微服务框架的Spring Security 身份认证系统架构,介绍了前后端框架分布情况,给出了使用Spring Data JPA 构建的数据持久化层及HTML+Thymeleaf 模板引擎搭建的前端视图模型。
关键词:SpringBoot 框架;Spring Security 框架;Spring Data JPA ;Thymeleaf ;身份认证;数据持久化642019.08Acegi Security,是Spring 项目组中用来提供安全认证服务的框架。
Spring 中的IOC,DI 是在项目启动阶段将配置文件中的固有信息结合代码中的特定标注信息提前写入内存中,创建相应的POJO 对象,为后期的资源调用提供输入,Spring 中的AOP 技术也是独立于项目架构主体的,满足诸如日志、身份认证、访问限制等功能的组件,其原理是优化J2EE 中的Filter (过滤器)、Listener (监听器)和Interceptor (拦截器),Spring Security 通过AOP 监控访问事件,当有访问请求时,查询用户访问资源权限,引导用户通过登录的途径获得相应资源的访问权限。
3Web 应用平台的身份认证系统实现Web 应用平台的身份认证系统是构建在Spring⁃Boot+Spring Security 微服务技术基础上的,前端采用HTML+Thym eleaf,数据持久层采用Spring Data JPA 框架,是依据项目主体对身份认证具有统一管理、高度安全、维护简单的要求而采用的框架集群。
3.1SpringBoot+Spring Security 身份认证系统架构图1展示了Web 应用平台的身份认证系统架构,当项目初始化构建时,Spring Security 框架通过注解配置操作,将用于认证用户身份的规则UserDetailsService 实例、Spring Data JPA 构建的JpaRepository 实例、密码加密PasswordEncoder 实例等加载进入核心库中,当有非法用户需要认证身份时,规则将对提交信息进行相应审核。
3.2Spring Security 核心技术-配置身份认证机制和资源访问规则Spring Security 安全框架的核心功能是对用户身份认证从而确定资源访问权限,当用户未登录访问某限定资源时将被安全框架指引到登录页面完成登录,认证通过后,资源将自动为用户开放,直到用户注销或会话结束。
@Configurationpublic class WebSecurityConfigu extends Web⁃SecurityCon figurerAdapter {protected void configure (Authentication Man⁃agerBuilder auth)throws Exception {}protected void configure (HttpSecurity http)throws Exception {http.authorizeRequests ().antMatchers ("success","/images/*","/","/css/","/js/*","/font/*","/index").permitAll().anyRequest ().authenticated ().and ().formLogin ().login⁃Page("/login").permitAll ().defaultSuccessUrl ("/success").permitAll ().failureUrl ("/error").permitAll ().and ().logout ().permitAll();}}configure 方法设置了身份认证机制,包含用户名认证及密码加密规则,通过AuthenticationManager Builder 对象auth 的userDetailsServer 方法实现用户身份认证,通过passwordEncoder 方法对用户提交的密码进行加密。
CustomUserService()方法可以自定义用户名认证策略,一般有内存认证、JDBC 认证、通用认证3种。
4结语针对于Spring+Spring Security 框架组合在身份认证管理方面存在的问题,提出了基于SpringBoot+SpringSe⁃curity 微服务技术体系的整体解决方案,从Web 应用平台的身份认证系统实现可以得出,Spring Security 技术体系在代码量上大幅减少,降低了开发成本,缩短了开发周期。
参考文献[1]Stephen Young.How to become a web developer part5:Web a,plica tion architecture [EB/OL].https:///web-appl ication-arch itecture-basics/,2015-01-13.[2].What is Microservices Architecture?[EB/OL],2016.图1身份认证系统架构图HTML 前端视图层Thymeleaf 模板引擎Spring Security 框架层WebSecurityConfigureAdapterUserDetailsServiceWebSecurityHttpSecurityAuthenticationManagerBuilderPasswordEncoder BCryptPasswordEncoderSpring Data JPA SpringMVC 控制器数据库驱动层数据库管理系统数据库文件系统服务器操作系统(下转第68页)652019.08[3]孙耀,刘杰.分布式文件系统元数据服务的负载均衡框架[J].软件学报,2016,27(12):3192-3207.[4]王亚楠,吴华瑞,等.高并发Web 应用系统的性能优化分析与研究[J].计算机工程与设计,2014,(8):2976-2980.[5]刘迪,等.基于强挂起弱预测机制的负载均衡模型研究[J].计算机工程与应用,2016,(8):1-7.[6]欧阳荣彬.基于微服务的数据服务框架设计[J].华中科技大学学报(自然科学版),2016,44(S1):126-130.(-0))Dim t1As DataTable=st.ExecuteReader If t1.DataRows.count <>0ThenDim tks As String =t1.GetComboListString("题库代码")Dim tkms()As String =tks.Split("|")Dim s(tkms.Length)As StringWith wb.AddListGroup ("","lsg1","今日错题统计"&Date.Today).add ("l1","<center><h2>错题复习</h2></cen⁃ter><h4>本页面是今日错题整合,请点击查看:<br>当前用户:["&ph &"]</h4>")For i As Integer=0To tkms.Length-1For Each x As DataRow In t1.DataRows If tkms(i)=x("题库代码")Then s(i)=s(i)+x("错题")End If NextDim ctlj As String =Functions.Execute("cuotichuli_cuotifenjie",tkms(i),s(i)).Add("ls"&i,"题库"&i+1&"->错题数"&ctlj.SubString(ctlj.IndexOf("&ts")+4),tkms(i),"/cuoti?"&ctlj)Next End With Elsewb.AppendHTML (Functions.Execute ("wx_tishi","今天你没有完成任何测试任务","测试后再来","测试","/homepage",""))End IfReturn wb.Build'错题分解函数,用于处理某一阶段错题'统计,去重,形成新错题链接'函数调用:cuotichuli_cuotifenjie Dim tkdh As String =args(0)Dim cts As String =Args(1)Dim cts_fj()As String =cts.split("_")Dim s As Integer=0Dim cts_jg As String =""For i As Integer =0To cts_fj.Length-1If cts_jg.IndexOf(cts_fj(i))<0Then cts_jg=cts_jg+"_"+cts_fj(i)s=s+1End If NextDim cuoti_jm As String ="ctzk="&urlencode(Encrypt⁃Text(cts_jg,Vars("setuserkey1"),Vars("sttuserkey2")))Dim ks_tk1As String ="&ks_tk ="&urlencode (En⁃cryptText(tkdh,Vars("setuserkey1"),Vars("sttuserkey2")))Dim cuoti_lj As String =cuoti_jm &ks_tk1Return cuoti_lj &"&ts=["&format(s,"000")&"]"6系统测试(1)适用网址:。