上一篇博客 SpringSecurity 实战项目(二)只是实现了用户、角色、权限的动态管理,但是其权限管理是有缺陷的,他不支持restful
风格的接口权限管理,因为他无法区分客户端的请求方式。
本片博客是为了弥补此缺陷的,本篇博客将在 SpringSecurity 实战项目(二)的基础上进行修改使其支持restful
风格的接口的权限管理。
源码地址:https://gitee.com/zenghua3300/SpringSecurity
喜欢的小伙伴麻烦帮点一个star
如果是刚接触security
的小伙伴,可以先看下这两篇基础博客
《Spring Security认证过程》
《Spring Security授权过程》
JWT全面解读、详细使用步骤
本文设计和代码是基于 以下博客(请点击)
SpringSecurity 实战项目(一)
SpringSecurity 实战项目(二)
进行修改。
首先分析一下工作量吧,因为要支持 restful
风格的接口,那么我们在判断用户是不是有权限访问的时候不仅要判断 url
还要判断请求方式。 所以我门需要修改数据库表,因为我门的权限表还没有method
字段。
由于要判断 url
和 method
所以要在CustomUserService
类的loadUserByUsername
方法中要添加 权限的url
和 method
。但是SimpleGrantedAuthority
只支持传入一个参数。 所以我门考虑要再写一个类 实现 GrantedAuthority
接口,并在构造函数中传入两个参数。嘻嘻。
由于我们不仅要判断url
还要 判断请求方法,所以当然要修改MyAccessDecisionManager
的decide
方法的内容了。
因为:
decide
方法是判定是否拥有权限的决策方法 ,三个参数的含义分别为:
//authentication
是释CustomUserService
中循环添加到GrantedAuthority
对象中的权限信息集合,相当于是自己定义的匹配规则,项目中把权限规则存在数据库Sys_permission
表中
//object
包含客户端发起的请求的requset
信息,可转换为HttpServletRequest request = ((FilterInvocation) object).getHttpRequest()
;
//configAttributes
为MyInvocationSecurityMetadataSource
的getAttributes(Object object)
这个方法返回的结果,此方法是为了判定用户请求的url
是否在权限表中,如果在权限表中,则返回给decide
方法,用来判定用户是否有此权限。如果不在权限表中则放行。这个项目中直接全部都拦截,全部都交给decide
方法
我们发现
object
为当前请求的url:/persons
, 那么getAttributes
方法就是使用当前的访问资源路径去匹配我们自己定义的匹配规则。
MyInvocationSecurityMetadataSourceService
的getAttributes
url
是否在权限表中,如果在权限表中,则返回给 decide
因为我不想每一次来了请求,都先要匹配一下权限表中的信息是不是包含此url, 我准备直接拦截,不管请求的url 是什么都直接拦截, 然后在MyAccessDecisionManager的decide 方法中做 拦截还是放行的决策。
可以说到目前为止这是最复杂的一个步骤,其实搞清楚了还是挺简单的,网上挺多人都更倾向于使用shiro
,但是偶尔也要尝试一下新东西的嘛,但是当时我在摸索的时候遇到挺多坑,当时也已经到了思考人生的地步了 框架不是为了简化开发吗!为什么!明明jwt加上权限框架是双倍的快乐!
回到正题,到底要怎么配置呢?使用过shiro
的人会知道,鉴权的话需要自己实现一个realm
,重写两个方法,第一是用户验证
,第二是鉴权
。在spring-security
中也不例外,这边需要实现两个过滤器。使用JWTAuthenticationFilter
去进行用户账号的验证,使用JWTAuthorizationFilter
去进行用户权限的验证。
JWTAuthenticationFilter
继承于UsernamePasswordAuthenticationFilter
该拦截器用于获取用户登录的信息,只需创建一个token
并调用authenticationManager.authenticate()
让spring-security
去进行验证就可以了,不用自己查数据库再对比密码了,这一步交给spring
去操作。
这个操作有点像是shiro
的subject.login(new UsernamePasswordToken())
,验证的事情交给框架。
下边会献上这一部分的代码。
验证成功当然就是进行鉴权了,每一次需要权限的请求都需要检查该用户是否有该权限去操作该资源,当然这也是框架帮我们做的,那么我们需要做什么呢?很简单,只要告诉spring-security
该用户是否已登录,是什么角色,拥有什么权限就可以了。
JWTAuthenticationFilter
继承于BasicAuthenticationFilter
,至于为什么要继承这个我也不太清楚了,这个我也是网上看到的其中一种实现,实在springSecurity
苦手,不过我觉得不继承这个也没事呢(实现以下filter
接口或者继承其他filter
实现子类也可以吧)只要确保过滤器的顺序,JWTAuthorizationFilter
在JWTAuthenticationFilter
后面就没问题了。
到这里基本操作都写好啦,现在就需要我们将这些辛苦写好的“组件”组合到一起发挥作用了,那就需要配置了。需要开启一下注解@EnableWebSecurity
然后再继承一下WebSecurityConfigurerAdapter
就可以啦,springboot
就是可以为所欲为~
csrf
restful
风格的接口单点登录
好了分析完了,接下来就是编码了。
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
添加method
字段,当然Permission
的java bean
中 也要添加此属性和其get set
方法。
//请求方法
private String method;
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
新建java
类MyGrantedAuthority
实现 GrantedAuthority
接口
package com.us.example.service;
import org.springframework.security.core.GrantedAuthority;
/**
* Created by yangyibo on 17/2/15.
*/
public class MyGrantedAuthority implements GrantedAuthority {
private String url;
private String method;
public String getPermissionUrl() {
return url;
}
public void setPermissionUrl(String permissionUrl) {
this.url = permissionUrl;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public MyGrantedAuthority(String url, String method) {
this.url = url;
this.method = method;
}
@Override
public String getAuthority() {
return this.url + ";" + this.method;
}
}
在CustomUserService
类中使用MyGrantedAuthority
public UserDetails loadUserByUsername(String username) {
SysUser user = userDao.findByUserName(username);
if (user != null) {
List<Permission> permissions = permissionDao.findByAdminUserId(user.getId());
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for (Permission permission : permissions) {
if (permission != null && permission.getName() != null) {
GrantedAuthority grantedAuthority = new MyGrantedAuthority(permission.getUrl(), permission.getMethod());
grantedAuthorities.add(grantedAuthority);
}
}
//返回JwtUser类型的对象
return new JwtUser(user.getUsername(), user.getPassword(), grantedAuthorities);
} else {
throw new UsernameNotFoundException("admin: " + username + " do not exist!");
}
}
package com.us.example.service;
import com.us.example.security.UrlGrantedAuthority;
import org.apache.log4j.Logger;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
/**
*/
@Service
public class MyAccessDecisionManager implements AccessDecisionManager {
static Logger logger = Logger.getLogger(MyAccessDecisionManager.class.getName());
//decide 方法是判定是否拥有权限的决策方法
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
String url, method;
if ("anonymousUser".equals(authentication.getPrincipal())
|| matchers("/images/**", request)
|| matchers("/js/**", request)
|| matchers("/css/**", request)
|| matchers("/fonts/**", request)
|| matchers("/", request)
|| matchers("/index.html", request)
|| matchers("/favicon.ico", request)
|| matchers("/login", request)) {
return;
} else {
for (GrantedAuthority ga : authentication.getAuthorities()) {
if (ga instanceof UrlGrantedAuthority) {
UrlGrantedAuthority urlGrantedAuthority = (UrlGrantedAuthority) ga;
url = urlGrantedAuthority.getPermissionUrl();
method = urlGrantedAuthority.getMethod();
if (matchers(url, request)) {
if (method.equals(request.getMethod()) || "ALL".equals(method)) {
return;
}
}
}
}
}
throw new AccessDeniedException("no right");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
private boolean matchers(String url, HttpServletRequest request) {
AntPathRequestMatcher matcher = new AntPathRequestMatcher(url);
if (matcher.matches(request)) {
return true;
}
return false;
}
}
package com.us.example.service;
import com.us.example.security.UrlConfigAttribute;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
@Service
public class MyInvocationSecurityMetadataSourceService implements
FilterInvocationSecurityMetadataSource {
//此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
final HttpServletRequest request = ((FilterInvocation) object).getRequest();
Set<ConfigAttribute> allAttributes = new HashSet<>();
ConfigAttribute configAttribute = new UrlConfigAttribute(request);
allAttributes.add(configAttribute);
return allAttributes;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
package com.us.example.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.us.example.domain.JwtUser;
import com.us.example.model.LoginUser;
import com.us.example.utils.JwtTokenUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private ThreadLocal<Integer> rememberMe = new ThreadLocal<>();
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
super.setFilterProcessesUrl("/auth/login");
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
LoginUser loginUser = new LoginUser();
try {
// 从表单中获取用户信息
if(request.getContentType().equals("application/x-www-form-urlencoded")){
String username = request.getParameter("username");
String password = request.getParameter("password");
loginUser.setUsername(username);
loginUser.setPassword(password);
loginUser.setRememberMe(1);
}else{
// 从输入流中获取用户信息
InputStream inputStream= request.getInputStream();
loginUser = new ObjectMapper().readValue(inputStream, LoginUser.class);
}
rememberMe.set(loginUser.getRememberMe());
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList<>())
);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
// 成功验证后调用的方法
// 如果验证成功,就生成token并返回
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult) throws IOException, ServletException {
System.out.println(authResult.getPrincipal().toString());
JwtUser jwtUser = (JwtUser) authResult.getPrincipal();
System.out.println("jwtUser:" + jwtUser.toString());
boolean isRemember = rememberMe.get() == 1;
List permissionurl_methodList = new ArrayList();
String role ="";
Collection<? extends GrantedAuthority> authorities = jwtUser.getAuthorities();
for (GrantedAuthority authority : authorities){
role = authority.getAuthority();
permissionurl_methodList.add(role);
}
String token = JwtTokenUtils.createToken(jwtUser.getUsername(), permissionurl_methodList, isRemember);
// String token = JwtTokenUtils.createToken(jwtUser.getUsername(), false);
// 返回创建成功的token
// 但是这里创建的token只是单纯的token
// 按照jwt的规定,最后请求的时候应该是 `Bearer token`
response.setHeader("token", JwtTokenUtils.TOKEN_PREFIX + token);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
response.getWriter().write("authentication failed, reason: " + failed.getMessage());
}
}
package com.us.example.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.us.example.exception.TokenIsExpiredException;
import com.us.example.security.UrlGrantedAuthority;
import com.us.example.utils.JwtTokenUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;
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.List;
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
UrlGrantedAuthority urlGrantedAuthority = new UrlGrantedAuthority();
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
String tokenHeader = request.getHeader(JwtTokenUtils.TOKEN_HEADER);
// 如果请求头中没有Authorization信息则直接放行了
if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)) {
chain.doFilter(request, response);
return;
}
// 如果请求头中有token,则进行解析,并且设置认证信息
try {
SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));
} catch (TokenIsExpiredException e) {
//返回json形式的错误信息
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
String reason = "统一处理,原因:" + e.getMessage();
response.getWriter().write(new ObjectMapper().writeValueAsString(reason));
response.getWriter().flush();
return;
}
super.doFilterInternal(request, response, chain);
}
// 这里从token中获取用户信息并新建一个token
private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) throws TokenIsExpiredException {
String token = tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, "");
boolean expiration = JwtTokenUtils.isExpiration(token);
if (expiration) {
throw new TokenIsExpiredException("token超时了");
} else {
String username = JwtTokenUtils.getUsername(token);
List role = JwtTokenUtils.getUserRole(token);
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
//把role里的值取出来,然后组装成UrlGrantedAuthority
for (int i =0;i<role.size();i++) {
if (role.get(i) != null) {
urlGrantedAuthority = SplitRole((String)role.get(i));
GrantedAuthority grantedAuthority = new UrlGrantedAuthority(urlGrantedAuthority.getPermissionUrl(),urlGrantedAuthority.getMethod());
grantedAuthorities.add(grantedAuthority);
}
}
if (username != null) {
return new UsernamePasswordAuthenticationToken(username, null, grantedAuthorities);
}
}
return null;
}
private UrlGrantedAuthority SplitRole(String urlG){
String[] roleString = StringUtils.split(urlG,";");
urlGrantedAuthority.setPermissionUrl(roleString[0]);
urlGrantedAuthority.setMethod(roleString[1]);
return urlGrantedAuthority;
}
}
package com.us.example.utils;
import com.us.example.exception.TokenIsExpiredException;
import com.us.example.filter.JWTAuthenticationFilter;
import io.jsonwebtoken.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public class JwtTokenUtils {
private static final Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);
public static final String TOKEN_HEADER = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
private static final String SECRET = "jwtsecretdemo";
private static final String ISS = "echisan";
// 角色的key
private static final String PERMISSIONURL_METHOD = "permissionurl_method";
// 过期时间是3600秒,既是1个小时
private static final long EXPIRATION = 3600L;
// 选择了记住我之后的过期时间为7天
private static final long EXPIRATION_REMEMBER = 604800L;
// 创建token
public static String createToken(String username, List permissionurl_method, boolean isRememberMe) {
long expiration = isRememberMe ? EXPIRATION_REMEMBER : EXPIRATION;
HashMap<String, Object> map = new HashMap<>();
map.put(PERMISSIONURL_METHOD, permissionurl_method);
return Jwts.builder()
.signWith(SignatureAlgorithm.HS512, SECRET)
.setClaims(map)
.setIssuer(ISS)
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.compact();
}
// 从token中获取用户名
public static String getUsername(String token) throws TokenIsExpiredException {
return getTokenBody(token).getSubject();
}
// 获取用户角色
public static List getUserRole(String token) throws TokenIsExpiredException {
return (List)getTokenBody(token).get(PERMISSIONURL_METHOD);
}
// 是否已过期
public static boolean isExpiration(String token) {
try {
return getTokenBody(token).getExpiration().before(new Date());
} catch (ExpiredJwtException e) {
return true;
} catch (TokenIsExpiredException e) {
e.printStackTrace();
return true;
}
}
private static Claims getTokenBody(String token) throws TokenIsExpiredException {
try{
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace("Bearer ", ""))
.getBody();
} catch (ExpiredJwtException e) {
logger.error("Token已过期: {} " + e);
throw new TokenIsExpiredException("Token已过期");
} catch (UnsupportedJwtException e) {
logger.error("Token格式错误: {} " + e);
throw new TokenIsExpiredException("Token格式错误");
} catch (MalformedJwtException e) {
logger.error("Token没有被正确构造: {} " + e);
throw new TokenIsExpiredException("Token没有被正确构造");
} catch (SignatureException e) {
logger.error("签名失败: {} " + e);
throw new TokenIsExpiredException("签名失败");
} catch (IllegalArgumentException e) {
logger.error("非法参数异常: {} " + e);
throw new TokenIsExpiredException("非法参数异常");
}
}
}
关于 什么是csrf
请看这篇博客
spring security CSRF 问题
修改 WebSecurityConfig
的configure(HttpSecurity http)
方法 ,添加 .csrf().disable();
添加两个过滤器JWTAuthenticationFilter
,JWTAuthorizationFilter
现在就需要我们将这些辛苦写好的“组件”组合到一起发挥作用了,那就需要配置了。需要开启一下注解@EnableWebSecurity
然后再继承一下WebSecurityConfigurerAdapter
就可以啦,springboot
就是可以为所欲为~
好了编码部分完成了
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
@Autowired
private CustomUserService customUserService;
//这里使用BCryptPasswordEncoder加密
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**", "/js/**","/images/**", "**/favicon.ico","/login.html").permitAll()
.anyRequest().authenticated() //任何请求,登录后可以访问
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.permitAll()
.and()
.logout()
.permitAll() //注销行为任意访问
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
// .exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint())
// .accessDeniedHandler(new JWTAccessDeniedHandler()); //添加无权限时的处理
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class)
.csrf().disable();
}
// public void configure(WebSecurity web) throws Exception {
// //解决静态资源被拦截的问题
// web.ignoring().antMatchers("/css/**");
// }
}
由于我们是要测试restful
风格的权限,所以我门要有restful
的接口
package com.us.example.controller;
import com.us.example.domain.Msg;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HomeController {
@RequestMapping("/")
public String index(Model model){
Msg msg = new Msg("测试标题","测试内容","欢迎来到HOME页面,您拥有 ROLE_HOME 权限");
model.addAttribute("msg", msg);
return "home";
}
@RequestMapping("/admin")
@ResponseBody
public String hello(){
return "hello admin";
}
@RequestMapping("/login")
public String login(){
return "login";
}
@RequestMapping(value = "/user", method = RequestMethod.GET)
@ResponseBody
public String getList(){
return "hello getList";
}
@RequestMapping(value = "/user/hello", method = RequestMethod.GET)
@ResponseBody
public String getHelloList(){
return "hello getList";
}
@RequestMapping(value = "/user", method = RequestMethod.POST)
@ResponseBody
public String save(){
return "hello save";
}
@RequestMapping(value = "/user", method = RequestMethod.PUT)
@ResponseBody
public String update(){
return "hello update";
}
}
在数据库
中添加测试数据
,主要是权限表
和权限角色中间表
。
结果(角色1 可以访问 /user 下的所有接口, 角色2 只可以访问 /user 下的GET请求)
权限表:
权限角色中间表:
(此处角色1
拥有 权限 6
,权限6
的方法为ALL
也就是角色6
可以访问所有路径为/user
的接口)
启动项目,然后在postman
中测试,
JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter
,默认的拦截登录路径是/auth/login
,这里我们配置了可以自定义的登录路径,可以根据自己的需求来写。例如:/login
。但这里security
配置了不拦截登录路径/login
,而我们前端表单提交的action
是/auth/login
,这样才会把数据提交到我们配置的拦截器JWTAuthenticationFilter
中,不然/login
每次都会被security
放过。访问http://localhost:8081/auth1/login
因为/auth1/login
不是登录路径,所以被security
拦截,并且设置上是ROLE_ANONYMOUS
,这是没有经过认证的角色,最后引导用户到登录页面登录
因为ROLE_ANONYMOUS
是没有经过身份认证的角色,所以引导用户进行登录
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
super.setFilterProcessesUrl("/auth/login");
}
访问http://localhost:8081/auth/login
,登录成功后获得一个token
Token
里边的信息
携带生成的token
去访问http://localhost:8081/admin
改用put
去访问http://localhost:8081/user
put
方法访问成功 。
登录abel
后,生成新的token
,用新的token
访问 user
的所有权限,只有GET
权限可以访问,put
方法访问失败。
参考资料:
springBoot+springSecurity 动态管理Restful风格权限(三)
Springboot+Spring-Security+JWT+Redis实现restful Api的权限管理以及token管理(超详细用爱发电版)