package shang.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.OncePerRequestFilter; import shang.handle.*; import shang.service.UserDetailsServiceImpl; import javax.sql.DataSource; /** * @Auther: 三尾鱼 * @Date: 2020/11/23 08:51 * @Description: */ //@EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) //开启注解方式,的鉴权判断, // 其中securedEnabled = true 是基于角色的注解; //prePostEnabled = true 是基于access表达式的注解,即可以角色,又可以权限因子 @Configuration public class SecurityConfig02 extends WebSecurityConfigurerAdapter { /* @Autowired DataSource dataSource; */ @Autowired UserDetailsServiceImpl userDetailsServiceImpl; @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService (userDetailsServiceImpl).passwordEncoder (passwordEncoder ()); } //要实现自定义登录页面,必须继承 WebSecurityConfigurerAdapter 并且重写 configure(HttpSecurity http) /** * anyRequest | 匹配所有请求路径 * access | SpringEl表达式结果为true时可以访问 * anonymous | 匿名可以访问 * denyAll | 用户不能访问 * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 * hasRole | 如果有参数,参数表示角色,则其角色可以访问 * permitAll | 用户可以任意访问 * rememberMe | 允许通过remember-me登录的用户访问 * authenticated | 用户登录后可访问 */ @Override protected void configure(HttpSecurity http) throws Exception { http.cors ().and ().csrf ().disable () //不要session .sessionManagement ().sessionCreationPolicy (SessionCreationPolicy.STATELESS); //未登陆时返回 JSON 格式的数据给前端,否则是html http.httpBasic ().authenticationEntryPoint (restAuthenticationEntryPoint); http.formLogin ()//表单提交 .usernameParameter ("username123")//自定义如蝉,缺省是username 和 password .passwordParameter ("password123")//如果登录页面中没有,登录都会失败 .loginPage ("/login")//自定义登录页面 都不要带后缀 //还要指定自定义登录的URL,必须和表单提交的接口一样,这样才能触发自定义登录逻辑 .loginProcessingUrl ("/login")//url /login 是login.html 页面的action //.successForwardUrl ("/toSuccess")//登录成功后,跳转的URL,需要post请求 //.successForwardUrl ("http://www.baidu.com")//这种方式行不通 //.successHandler (new 自定义成功跳转逻辑类 ("http://www.baidu.com")) .successHandler (new 登录成功处理器 ("/toSuccess"))//登录成功处理器 //真正的前后端分离开发,这里无论成功与否,都返回JSON字符串和令牌,让前端自己决定如何处理 //.failureForwardUrl ("/toError");//登录失败,跳转的URL ,需要post请求 .failureHandler (new 登录失败处理器 (""));//登录失败处理器 // 【异常处理】之 鉴权失败处理器 要另起一行单独配置 http.exceptionHandling ().accessDeniedHandler (new 鉴权失败处理器 ()); //加入自定义授权过滤器 http.addFilter (new JWTAuthorizationFilter (authenticationManager ())); //实践证明:如果不把凭证放进spring security的上下文,将访问不到任何需要认证才放行的URL, // 自定义此拦截器的就是为了在每次请求都把Head中的token解密后的用户凭证放进上下文 //至于权限验证,交给框架完成,当然如果你想自定义就覆盖框架的相应过滤器 //这2个过滤器是等价的 //http.addFilterBefore (new 每次请求拦截一次过滤器 (), UsernamePasswordAuthenticationFilter.class); //开启记住我 功能 /*http.rememberMe () //设置数据源 .tokenRepository (persistentTokenRepository ()) //设置超时时间,缺省是2周,这里设置60秒 .tokenValiditySeconds (60) //自定义登录逻辑,必选项 .userDetailsService (userDetailsServiceImpl);*/ //退出配置 http.logout () //退出成功后,跳转的页面 .logoutSuccessUrl ("/login"); //授权 http.authorizeRequests () //antMatchers方法中的通配符?=任何一个字符 *=任意多个字符 **=任意多个目录 //.antMatchers ("/js/**", "/css/**", "/img/**").permitAll () //放行所有静态资源 //.antMatchers ("/**/*.png").permitAll () //放行所有后缀是png的图片资源 .regexMatchers (".+\\.png").permitAll () //使用正则表达式匹配 .regexMatchers (HttpMethod.POST, "/demo").permitAll () //必须是post请求 .antMatchers ("/login").permitAll ()//必须放行登录URL,否则会报 重定向次数过多 .antMatchers ("/toError").permitAll ()//必须放行登录失败的URL,否则会报 重定向次数过多 //.antMatchers ("/admin").hasAuthority ("admin")//严格区分大小写,基于权限因子的访问控制 //.antMatchers ("/admin").hasAnyAuthority ("admin","Admin") //hasAnyAuthority 严格区分大小写,基于权限因子的访问控制,有任意一个权限因子都可以访问 //基于角色的访问控制,严格区分大小写,授权失败,如何返回JSON字符串 //.antMatchers ("/admin").hasAnyRole ("Admin","useR")//注意点:角色名,不用添加前缀【ROLE_】 //.antMatchers ("/admin").access ("hasAnyRole(‘admin1‘,‘user1‘)") //基于IP地址的访问控制 //.antMatchers ("/admin").hasIpAddress ("227.0.0.1") //所有请求都必须认证,就是必须登录,才能访问 .anyRequest ().authenticated (); //自定义的access方法 //.anyRequest ().access ("@myAccessControImpl.hasPermission(request,authentication)"); } @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder (); } //记住我的时候,添加的这个 /*@Bean public PersistentTokenRepository persistentTokenRepository() { JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl (); //设置数据源 tokenRepository.setDataSource (dataSource); //设置 自动建表,第一次使用,第二次注释,否则每次建表会覆盖掉原来记录 tokenRepository.setCreateTableOnStartup (false); return tokenRepository; }*/ //容许跨站请求 @Bean CorsConfigurationSource corsConfigurationSource() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource (); source.registerCorsConfiguration ("/**", new CorsConfiguration ().applyPermitDefaultValues ()); return source; } }
package shang.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import shang.entity.JsonMsg;
/**
* @Auther: 三尾鱼
* @Date: 2021/2/19 12:33
* @Description:
*/
@Controller
public class HomeController
{
@RequestMapping("/login")
public ModelAndView login(ModelAndView mv)
{
mv.setViewName ("/login");//设置视图名
return mv;
}
//@Secured ("ROLE_user3")
//@PreAuthorize ("hasRole(‘user‘)")
//@PreAuthorize ("hasRole(‘ROLE_user‘)") //可以使用ROLE_ 前缀
@PreAuthorize ("hasAnyAuthority(‘admin‘,‘user‘)")//基于权限因子的 鉴权
@RequestMapping("/toSuccess")
public ModelAndView toSuccess(ModelAndView mv)
{
mv.setViewName ("/main");//设置视图名
return mv;
}
// @RequestMapping("/toError")
// public ModelAndView toError(ModelAndView mv)
// {
// mv.setViewName ("/error");
// 设置视图名,如果templates下面有特别error.html 页面,发生权限不够,将自动关联到error.html
//如果没有error.html 将显示403
// return mv;
// }
@RequestMapping("/demo")
@ResponseBody
public JsonMsg demo()
{
JsonMsg jsonMsg =new JsonMsg ();
jsonMsg.setMsg ("张三");
jsonMsg.setStatusCode (200);
return jsonMsg;
}
@PreAuthorize ("hasAnyAuthority(‘admin‘,‘user33‘)")//基于权限因子的 鉴权
@RequestMapping("/admin")
public String admin(){
return "admin";
}
@RequestMapping("/showspringsecurity5")
public String show_springsecurity5(){
return "show-springsecurity5";
}
}
package shang.handle;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
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;
/**
* @Auther: Administrator
* @Date: 2021/2/21 14:19
* @Description:
*/
public class JWTAuthorizationFilter extends BasicAuthenticationFilter
{
public JWTAuthorizationFilter(AuthenticationManager authenticationManager)
{
super (authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException
{
//String tokenHeader = "123";//request.getHeader(JwtTokenUtils.TOKEN_HEADER);
// 如果请求头中没有Authorization信息则直接放行了
//if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)) {
/* if (tokenHeader == null || tokenHeader.equals ("123"))
{
//抛出鉴权失败异常
//throw new AccessDeniedException("403,无token");
//实践怎么,拿不到token,就返回到登陆页面
chain.doFilter (request, response);
return;
}*/
//chain.doFilter (request, response);
// 如果请求头中有token,则进行解析,并且设置认证信息
//SecurityContextHolder.getContext ().setAuthentication (getAuthentication (tokenHeader));
//super.doFilterInternal (request, response, chain);
//测试做个简易版
String authHeader =null;
try
{
authHeader =request.getHeader("token");//Authorization
//token里面包含有1、用户名 2、权限因子和角色
if (authHeader == null || authHeader.trim ().length () == 0)
{
}
else {
String username = "admin";
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken (username, null, AuthorityUtils.commaSeparatedStringToAuthorityList
("admin,normal,ROLE_admin,ROLE_user,/toSuccess,/insert,/delete"));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
//new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities ());
}catch (AuthenticationException e) {
System.out.println ("AuthenticationException="+e.getMessage ());
}
finally
{
//实践证明:如果不把凭证放进spring security的上下文,将访问不到任何需要认证才放行的URL,
// 自定义此拦截器的就是为了在每次请求都把Head中的token解密后的用户凭证放进上下文
//至于权限验证,交给框架完成,当然如果你想自定义就覆盖框架的相应过滤器
chain.doFilter(request, response);//如果上下文有认证信息,就跳过认证页面进入鉴权,如果没有认证信息,直接重定向到认证页面
}
}
// 这里从token中获取用户信息并新建一个token
private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader)
{
String token = "123";// tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, "");
String username = "admin";// JwtTokenUtils.getUsername(token);
if (username != null)
{
return new UsernamePasswordAuthenticationToken (username, null, new ArrayList<> ());
}
return null;
}
}
package shang.handle; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; import shang.entity.JsonMsg; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @Auther: Administrator * @Date: 2021/2/21 19:49 * @Description:这里为了之后结果更直观,自定义一个AuthenticationEntryPoint, * 用于在未登录是访问接口返回json而不是login.html * 当未登录或者token失效访问接口时,自定义的返回结果 */ @Component public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { response.setCharacterEncoding("UTF-8");//设置编码格式 response.setContentType("application/json"); JsonMsg jsonMsg =new JsonMsg (); jsonMsg.setMsg (" AuthenticationException 【尚未登录,或者登录过期】触发了-->"+e.getMessage()); jsonMsg.setStatusCode (403); //Jackson ObjectMapper mapper = new ObjectMapper (); String msgJson=mapper.writeValueAsString (jsonMsg); //response.getWriter().println(JSON.toJSONString(Result.error().message("尚未登录,或者登录过期 " + e.getMessage()))); response.getWriter ().print (msgJson); response.getWriter().flush(); } }
package shang.handle; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import shang.entity.JsonMsg; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @Auther: 三尾鱼 * @Date: 2021/2/19 14:54 * @Description: */ public class 登录失败处理器 implements AuthenticationFailureHandler { private String url; public 登录失败处理器(String url) { this.url = url; } @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { //httpServletResponse.sendRedirect (url); JsonMsg jsonMsg =new JsonMsg (); jsonMsg.setMsg ("自定义失败处理器 onAuthenticationFailure 触发了"); jsonMsg.setStatusCode (302); //Jackson ObjectMapper mapper = new ObjectMapper (); String msgJson=mapper.writeValueAsString (jsonMsg); //有中文乱码,设置一下 httpServletResponse.setHeader("Content-type", "application/json;charset=UTF-8"); //OutputStream ps = httpServletResponse.getOutputStream (); //这句话的意思,使得放入流的数据是utf8格式 //ps.write (personJson.getBytes (StandardCharsets.UTF_8)); httpServletResponse.getWriter ().print (msgJson); //httpServletResponse.sendRedirect (url); } }
package shang.handle; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.User; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import shang.entity.JsonMsg; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @Auther: 三尾鱼 * @Date: 2021/2/19 14:36 * @Description: */ public class 登录成功处理器 implements AuthenticationSuccessHandler { private String url; public 登录成功处理器(String url) { this.url = url; } /*@Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException { }*/ @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { User user = (User) authentication.getPrincipal (); System.out.println (user.getUsername ()); System.out.println (user.getPassword ()); System.out.println (user.getAuthorities ());//权限列表,就是UserDetailsServiceImpl放进去的【权限、角色】 数据 System.out.println (httpServletRequest.getRemoteAddr ()); JsonMsg jsonMsg =new JsonMsg (); jsonMsg.setMsg ("onAuthenticationSuccess 触发了--token:"+"abcd1234admin"); jsonMsg.setStatusCode (200); //Jackson ObjectMapper mapper = new ObjectMapper (); String personJson=mapper.writeValueAsString (jsonMsg); //有中文乱码,设置一下 httpServletResponse.setHeader("Content-type", "application/json;charset=UTF-8"); //OutputStream ps = httpServletResponse.getOutputStream (); //这句话的意思,使得输出流的数据是utf8格式 //ps.write (personJson.getBytes (StandardCharsets.UTF_8)); httpServletResponse.getWriter ().print (personJson); //httpServletResponse.sendRedirect (url); } }
package shang.handle; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; /** * @Auther: 三尾鱼 * @Date: 2021/2/19 17:26 * @Description: 前后端分离的开发 都是要返回JSON */ public class 鉴权失败处理器 implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { httpServletResponse.setStatus (httpServletResponse.SC_FORBIDDEN);//403 //有中文乱码,设置一下 httpServletResponse.setHeader("Content-type", "application/json;charset=UTF-8"); PrintWriter pw=httpServletResponse.getWriter (); Map<String,Object> map=new HashMap<String,Object> (); map.put ("status",403); map.put ("msg", "权限不够,请联系管理员"); ObjectMapper mapper = new ObjectMapper (); String JsonStr=mapper.writeValueAsString(map); pw.write (JsonStr); //pw.write ("{\"status\":\"error\",\"msg\":\"权限不够,请联系管理员\"} "); pw.flush (); pw.close(); } }
package shang.service; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Date; /** * @Auther: Administrator * @Date: 2021/2/21 19:37 * @Description: */ @Component public class JwtUtils { private static final String CLAIM_KEY_USERNAME = "sub"; private static final String CLAIM_KEY_CREATED = "created"; @Value("${jwt.secret}") private String secret; @Value("${jwt.expiration}") private Long expiration; // 创建token public String generateToken(String username) { return Jwts.builder () .signWith (SignatureAlgorithm.HS512, secret) .setSubject (username) .setIssuedAt (new Date ()) .setExpiration (new Date (System.currentTimeMillis () + expiration * 1000)) .compact (); } // 从token中获取用户名 public String getUserNameFromToken(String token) { return getTokenBody (token).getSubject (); } // 是否已过期 public boolean isExpiration(String token) { return getTokenBody (token).getExpiration ().before (new Date ()); } private Claims getTokenBody(String token) { return Jwts.parser () .setSigningKey (secret) .parseClaimsJws (token) .getBody (); } }
package shang.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; /** * @Auther: 三尾鱼 * @Date: 2020/11/24 12:20 * @Description: * USE ipranDB * GO * CREATE UNIQUE NONCLUSTERED --唯一 非聚集 * INDEX username_唯一_user on --索引名称 * [ipranDB].[dbo].[user] --表名称 * (username) -- 表的字段名称CREATE UNIQUE NONCLUSTERED --唯一 非聚集 * GO * */ @Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username //username 是前端传递过来的 ) throws UsernameNotFoundException { System.out.println ("执行了自定义登录逻辑!!"); //这里注入数据库访问类,可以改造成按数据库验证账号和口令 //需要从数据库获取的信息有:1、用户名,2、加密后的口令 3、权限因子或角色 //1、根据用户名到数据库去查询,返回UserDetails的实现类 /*User user; if (user == null) { throw new UsernameNotFoundException ("用户不存在!"); }*/ if(!"admin".equals (username)){ throw new UsernameNotFoundException ("用户不存在!"); } //2、比较密码(注册时已经加密过),如果匹配成功返回UserDetails, String password=passwordEncoder.encode("123"); //从数据库获取当前用户的所有角色 //3、这里的user是org.springframework.security.core.userdetails.User 它实现了UserDetails接口 //这里的"admin,normal"是权限因子,如果是角色,必须是【ROLE_】前缀开头,例如 ROLE_admin,ROLE_user return new User (username, password, AuthorityUtils.commaSeparatedStringToAuthorityList ("admin,normal,ROLE_admin,ROLE_user,/toSuccess,/insert,/delete")); //user.setRoles (userMapper.getUserRolesByUid (user.getId ())); //return user; } }
package shang.service; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.util.Collection; /** * @Auther: 三尾鱼 * @Date: 2021/2/20 09:34 * @Description: */ @Service public class MyAccessControImpl implements I自定义权限控制接口 { //类名,不可写中文,否则配置不进去 @Override public boolean hasPermission(HttpServletRequest request, Authentication authentication) { Object principal = authentication.getPrincipal (); if (principal instanceof UserDetails) { UserDetails userDetails = (UserDetails) principal; //获取当前登录用户的授权集合列表 Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities (); return authorities.contains (new SimpleGrantedAuthority (request.getRequestURI ()));//基于访问URL的权限控制 } return false; } }
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver #指定url,xxx是数据库的名字,serverTimezone=Asia/Shanghai 解决时区问题,&useUnicode=true&characterEncoding=utf-8指定字符的编码集 # jdbc-url: jdbc:mysql://localhost:3306/ipran_db?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8 # username: root # password: aatl?451 url: jdbc:mysql://localhost:3306/ipran_db?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8 username: root password: aatl?451 jwt: tokenHeader: Authorization #JWT存储的请求头 secret: my-springsecurity-plus #JWT加解密使用的密钥 expiration: 604800 #JWT的超期限时间(60*60*24*7) tokenHead: ‘Bearer ‘ #JWT负载中拿到开头,空格别忘了
spring boot之 springSecurity 结合JWT
原文:https://www.cnblogs.com/shang44/p/14429555.html