前言
Spring Security
现已成为java
后台权限校验的榜首挑选.今日就经过读代码的办法带咱们深入了解一下Security,本文首要是依据开源项目spring-boot-3-jwt-security来讲解Spring Security + JWT(Json Web Token).完成用户鉴权,以及权限校验.
一切代码依据jdk17+
构建.现在让咱们开始吧!
技能简介
Springboot 3.0
Spring Security
Json Web Token(JWT)
BCrypt
Maven
项目构建
- 项目运用
postgresql
数据库来存储用户信息以及Token
(为啥不用Redis?这个先挖个坑),能够按照自己的主意替换成mysql
数据库 - 拜访数据库运用的是
jpa
,关于一些简单的sql能够依据办法名主动映射,还是很便利的.没运用过的也没关系.不影响阅读今日的文章,后续能够依据自己的实践需求替换成mybatis-lpus
等 - 本文运用了Lombok来生成固定的模版代码
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.alibou</groupId>
<artifactId>security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>security</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- spring security 安全结构 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- web 依靠 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 数据库 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
<!-- doc 这个不需求的能够去掉 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.1.0</version>
</dependency>
<!-- 校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
项目装备
鉴权装备
- 当项目引进
Security
依靠后,发动项目会生成一个随机的暗码,当咱们要拜访资源的时分需求运用这个暗码登录后才干运用.这会影响咱们许多功用的正常运用,比方万恶的swagger
.下面咱们来具体了解怎样装备咱们需求鉴权的途径,以及需求放行的途径
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity
public class SecurityConfiguration {
private final JwtAuthenticationFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
private final LogoutHandler logoutHandler;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf()
.disable() //关闭csrf(跨域)
.authorizeHttpRequests()
//装备需求放行的途径
.requestMatchers(
"/api/v1/auth/**",
"/v2/api-docs",
"/v3/api-docs",
"/v3/api-docs/**",
"/swagger-resources",
"/swagger-resources/**",
"/configuration/ui",
"/configuration/security",
"/swagger-ui/**",
"/webjars/**",
"/swagger-ui.html"
)
.permitAll() //放行上述的一切途径
/*
* 权限校验(需求登录的用户有指定的权限才干够)
* requestMatchers: 指定需求阻拦的途径
* hasAnyAuthority: 指定需求的权限
*/
.requestMatchers("/api/v1/management/**").hasAnyRole(ADMIN.name(), MANAGER.name())
.requestMatchers(GET, "/api/v1/management/**").hasAnyAuthority(ADMIN_READ.name(), MANAGER_READ.name())
.requestMatchers(POST, "/api/v1/management/**").hasAnyAuthority(ADMIN_CREATE.name(), MANAGER_CREATE.name())
.requestMatchers(PUT, "/api/v1/management/**").hasAnyAuthority(ADMIN_UPDATE.name(), MANAGER_UPDATE.name())
.requestMatchers(DELETE, "/api/v1/management/**").hasAnyAuthority(ADMIN_DELETE.name(), MANAGER_DELETE.name())
.anyRequest()
.authenticated() //设置一切的恳求都需求验证
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) //运用无状况Session
.and()
.authenticationProvider(authenticationProvider)
//添加jwt过滤器
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
//设置logout(当调用这个接口的时分, 会调用logoutHandler的logout办法)
.logout()
.logoutUrl("/api/v1/auth/logout")
.addLogoutHandler(logoutHandler)
.logoutSuccessHandler((request, response,authentication) -> SecurityContextHolder.clearContext())
;
return http.build();
}
}
- 上述代码首要完成了四块功用分别是:
- 放行不需求鉴权的途径(注册&登录,swagger)
- 装备拜访特定的接口用户需求的权限.(比方想要删去用户有必要要有删去用户的权限)
- 添加前置过滤器,用来从Token中判别用户是否合法和获取用户权限:
jwtAuthFilter
- 装备退出登录的Handler,以及监听的途径.当拜访这个途径的时分会主动调用
logoutHandler
中的办法
登录装备
上面说到了权限和token
校验,咱们先来了解一下登录的逻辑是什么样的.在security
中需求一个UserDetails
类来定义用户账户的行为.这个是用户鉴权的要害.首要有账户,暗码,权限,用户状况等等.在下面代码中有具体的注释
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "_user")
public class User implements UserDetails {
@Id
@GeneratedValue
private Integer id; //主键ID
private String firstname; //名字
private String lastname; //姓氏
private String email; //邮箱
private String password; //暗码
/**
* 人物枚举
*/
@Enumerated(EnumType.STRING)
private Role role;
/**
* 用户关联的Token
* 这儿面运用了jpa的一对多映射
*/
@OneToMany(mappedBy = "user")
private List<Token> tokens;
/**
* 获取用户的权限
* 这儿是依据人物枚举的权限来获取的(静态的而非从数据库动态读取)
* @return 用户权限列表
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return role.getAuthorities();
}
/**
* 获取用户暗码
* 首要是用来指定你的password字段
* @return 用户暗码
*/
@Override
public String getPassword() {
return password;
}
/**
* 获取用户账号
* 这儿运用email做为账号
* @return 用户账号
*/
@Override
public String getUsername() {
return email;
}
/**
* 账号是否未过期,下面的这个几个办法都是用来指定账号的状况的,由于该项目是一个Demo,所以这儿悉数回来true
* @return true 未过期
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 账号是否未锁定
* @return true 未锁定
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 暗码是否未过期
* @return true 未过期
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 账号是否激活
* @return true 已激活
*/
@Override
public boolean isEnabled() {
return true;
}
}
在了解用户实体之后,咱们来看一下是怎样来进行登录装备的.怎样运用securty
来帮咱们办理用户暗码的校验.下面咱们来看一下security
的全体装备
@Configuration
@RequiredArgsConstructor
public class ApplicationConfig {
/**
* 拜访用户数据表
*/
private final UserRepository repository;
/**
* 获取用户详情Bean
* 依据email查询是否存在用户,假如不存在throw用户未找到反常
*/
@Bean
public UserDetailsService userDetailsService() {
//调用repository的findByEmail办法,来获取用户信息,假如存在则回来,假如不存在则抛出反常
return username -> repository.findByEmail(username)
//这儿运用的Option的orElseThrow办法,假如存在则回来,假如不存在则抛出反常
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
/**
* 身份验证Bean
* 传入获取用户信息的bean & 暗码加密器
* 能够回看一下SecurityConfiguration中 AuthenticationProvider的装备,运用的便是这儿注入到容器中的Bean
* 这个bean 首要是用于用户登录时的身份验证,当咱们登录的时分security会帮咱们调用这个bean的authenticate办法
*/
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
//设置获取用户信息的bean
authProvider.setUserDetailsService(userDetailsService());
//设置暗码加密器
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
/**
* 身份验证办理器
*/
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
/**
* 暗码加密器
* 首要是用来指定数据库中存储暗码的加密办法,确保暗码非明文存储
* 当security需求进行暗码校验时,会把恳求传进来的暗码进行加密,然后和数据库中的暗码进行比对
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
上述代码首要做了两件事:
- 指定咱们怎样从数据库中依据用户账号获取用户信息
- 指定用户暗码的加密器
passwordEncoder
现在咱们可能会存在一个疑问,security
怎样知道User
实体中那个字段是我的账户,那个字段是我的暗码?
不知道咱们是否记住UserDetails
类,也便是咱们的User
类.其中有两个办法getPassword
& getUsername
.这两个办法回来的便是账号和暗码.User
类中的还有几个其他的办法,能够依据咱们实践的业务需求来对账号进行禁用
等操作.
Token怎样生成
token
的生成首要是运用工具包来完成,在本项目中Token中首要存储用户信息
& 用户权限
,下面咱们先看一下token
工具包的代码.首要包含为: 生成token
,从token
中获取信息,以及验证token
@Service
public class JwtService {
/**
* 加密盐值
*/
@Value("${application.security.jwt.secret-key}")
private String secretKey;
/**
* Token失效时刻
*/
@Value("${application.security.jwt.expiration}")
private long jwtExpiration;
/**
* Token改写时刻
*/
@Value("${application.security.jwt.refresh-token.expiration}")
private long refreshExpiration;
/**
* 从Token中获取Username
* @param token Token
* @return String
*/
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
/**
* 从Token中回去数据,依据传入不同的Function回来不同的数据
* eg: String extractUsername(String token)
*/
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
/**
* 生成Token无额定信息
*/
public String generateToken(UserDetails userDetails) {
return generateToken(new HashMap<>(), userDetails);
}
/**
* 生成Token,有额定信息
* @param extraClaims 额定的数据
* @param userDetails 用户信息
* @return String
*/
public String generateToken(
Map<String, Object> extraClaims,
UserDetails userDetails
) {
return buildToken(extraClaims, userDetails, jwtExpiration);
}
/**
* 生成改写用的Token
* @param userDetails 用户信息
* @return String
*/
public String generateRefreshToken(
UserDetails userDetails
) {
return buildToken(new HashMap<>(), userDetails, refreshExpiration);
}
/**
* 构建Token办法
* @param extraClaims 额定信息
* @param userDetails //用户信息
* @param expiration //失效时刻
* @return String
*/
private String buildToken(
Map<String, Object> extraClaims,
UserDetails userDetails,
long expiration
) {
return Jwts
.builder()
.setClaims(extraClaims) //body
.setSubject(userDetails.getUsername()) //主题数据
.setIssuedAt(new Date(System.currentTimeMillis())) //设置发布时刻
.setExpiration(new Date(System.currentTimeMillis() + expiration)) //设置过期时刻
.signWith(getSignInKey(), SignatureAlgorithm.HS256) //设置摘要算法
.compact();
}
/**
* 验证Token是否有用
* @param token Token
* @param userDetails 用户信息
* @return boolean
*/
public boolean isTokenValid(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
}
/**
* 判别Token是否过去
*/
private boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
/**
* 从Token中获取失效时刻
*/
private Date extractExpiration(String token) {
//通用办法,传入一个Function,回来一个T
return extractClaim(token, Claims::getExpiration);
}
/**
* 从Token中获取一切数据
*/
private Claims extractAllClaims(String token) {
return Jwts
.parserBuilder()
.setSigningKey(getSignInKey())
.build()
.parseClaimsJws(token)
.getBody();
}
/**
* 获取签名Key
* Token 加密解密运用
*/
private Key getSignInKey() {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
return Keys.hmacShaKeyFor(keyBytes);
}
}
注册和登录
token
的生成现已看过了,下面该进入最要害的环节了.用户注册
& 用户登录
- 用户注册: 接收到用户传递过来的信息,在数据库中生成用户信息(暗码会经过
passwordEncoder
进行加密).用户信息保存成功后,会依据用户信息创建一个鉴权token
和一个refreshToken
- 用户登录: 获取到用户传递的账号暗码后,会创建一个
UsernamePasswordAuthenticationToken
目标.然后经过authenticationManager
的authenticate
办法进行校验,假如出现过错会依据过错的不同抛出不同的反常.在实践开发中能够经过捕获的反常类型不同来创建呼应提示.
@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
public class AuthenticationController {
private final AuthenticationService service;
/**
* 注册办法
* @param request 恳求体
* @return ResponseEntity
*/
@PostMapping("/register")
public ResponseEntity<AuthenticationResponse> register(
@RequestBody RegisterRequest request
) {
return ResponseEntity.ok(service.register(request));
}
/**
* 鉴权(登录办法)
* @param request 恳求体
* @return ResponseEntity
*/
@PostMapping("/authenticate")
public ResponseEntity<AuthenticationResponse> authenticate(
@RequestBody AuthenticationRequest request
) {
return ResponseEntity.ok(service.authenticate(request));
}
/**
* 改写token
* @param request 恳求体
* @param response 呼应体
* @throws IOException 反常
*/
@PostMapping("/refresh-token")
public void refreshToken(
HttpServletRequest request,
HttpServletResponse response
) throws IOException {
service.refreshToken(request, response);
}
}
能够看出来controller
中的办法都是对service
办法的调用,咱们现在看一下service
中的代码
@Service
@RequiredArgsConstructor
public class AuthenticationService {
private final UserRepository repository; //拜访user数据库
private final TokenRepository tokenRepository; //拜访token数据库
private final PasswordEncoder passwordEncoder; //暗码加密器
private final JwtService jwtService; //JWT 相关办法
private final AuthenticationManager authenticationManager; //Spring Security 认证办理器
/**
* 注册办法
* @param request 恳求体
* @return AuthenticationResponse(自己封装的呼应结构)
*/
public AuthenticationResponse register(RegisterRequest request) {
//构建用户信息
var user = User.builder()
.firstname(request.getFirstname())
.lastname(request.getLastname())
.email(request.getEmail())
.password(passwordEncoder.encode(request.getPassword()))
.role(request.getRole())
.build();
//将用户信息保存到数据库
var savedUser = repository.save(user);
//经过JWT办法生成Token
var jwtToken = jwtService.generateToken(user);
//生成RefreshToken(改写Token运用)
var refreshToken = jwtService.generateRefreshToken(user);
//将Token保存到数据库
saveUserToken(savedUser, jwtToken);
//回来呼应体
return AuthenticationResponse.builder()
.accessToken(jwtToken)
.refreshToken(refreshToken)
.build();
}
/**
* 鉴权(登录)办法
* @param request 恳求体
* @return AuthenticationResponse(自己封装的呼应结构)
*/
public AuthenticationResponse authenticate(AuthenticationRequest request) {
//经过Spring Security 认证办理器进行认证
//假如认证失败会抛出反常 eg:BadCredentialsException 暗码过错 UsernameNotFoundException 用户不存在
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getEmail(),
request.getPassword()
)
);
//经过邮箱查询用户信息,当时项目email便是账号
var user = repository.findByEmail(request.getEmail())
.orElseThrow();
//经过JWT办法生成Token
var jwtToken = jwtService.generateToken(user);
//生成RefreshToken(改写Token运用)
var refreshToken = jwtService.generateRefreshToken(user);
//将之前一切的Token变成失效状况
revokeAllUserTokens(user);
//保存新的Token到数据库
saveUserToken(user, jwtToken);
//封装呼应体
return AuthenticationResponse.builder()
.accessToken(jwtToken)
.refreshToken(refreshToken)
.build();
}
/**
* 保存用户Token办法
* 构建Token实体后保存到数据库
* @param user 用户信息
* @param jwtToken Token
*/
private void saveUserToken(User user, String jwtToken) {
var token = Token.builder()
.user(user)
.token(jwtToken)
.tokenType(TokenType.BEARER)
.expired(false)
.revoked(false)
.build();
tokenRepository.save(token);
}
/**
* 将用户一切Token变成失效状况
* @param user 用户信息
*/
private void revokeAllUserTokens(User user) {
//获取用户一切有用的token
var validUserTokens = tokenRepository.findAllValidTokenByUser(user.getId());
if (validUserTokens.isEmpty()){
return;
}
//假如存在还为失效的token,将token置为失效
validUserTokens.forEach(token -> {
token.setExpired(true);
token.setRevoked(true);
});
tokenRepository.saveAll(validUserTokens);
}
/**
* 改写token办法
* @param request 恳求体
* @param response 呼应体
* @throws IOException 抛出IO反常
*/
public void refreshToken(
HttpServletRequest request,
HttpServletResponse response
) throws IOException {
//从恳求头中获取中获取鉴权信息 AUTHORIZATION
final String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
final String refreshToken;
final String userEmail;
//假如鉴权信息为空或许不是以Bearer 最初的,直接回来
if (authHeader == null ||!authHeader.startsWith("Bearer ")) {
return;
}
//从鉴权信息中获取RefreshToken
refreshToken = authHeader.substring(7);
//从RefreshToken中获取用户信息
userEmail = jwtService.extractUsername(refreshToken);
if (userEmail != null) {
//依据用户信息查询用户,假如用户不存在抛出反常
var user = this.repository.findByEmail(userEmail)
.orElseThrow();
//验证Token是否有用
if (jwtService.isTokenValid(refreshToken, user)) {
//生成新的Token
var accessToken = jwtService.generateToken(user);
revokeAllUserTokens(user);
saveUserToken(user, accessToken);
//生成新的Token和RefreshToken并经过呼应体回来
var authResponse = AuthenticationResponse.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
new ObjectMapper().writeValue(response.getOutputStream(), authResponse);
}
}
}
}
上述代码首要说明了,注册
& 登录
后回来token
的流程,当时项目中由于token
& refreshToken
有用期较长所以挑选了将token
保存到数据库(个人观点!!!).能够依据自己业务的实践需求来决定是否需求保存到redis
恳求过滤
恳求过滤首要是在每次恳求的时分动态解析token
来获取用户信息
以及权限
,来确保恳求资源的安全性.避免越权拜访等.
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final UserDetailsService userDetailsService;
private final TokenRepository tokenRepository;
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
//判别恳求是否为登录恳求,假如是登录恳求则不进行处理
if (request.getServletPath().contains("/api/v1/auth")) {
filterChain.doFilter(request, response);
return;
}
//从恳求头中获取鉴权authHeader
final String authHeader = request.getHeader("Authorization");
final String jwt;
final String userEmail;
//假如不存在Token或许Token不已Bearer最初,则不进行处理
if (authHeader == null ||!authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
//从authHeader中截取出Token信息
jwt = authHeader.substring(7);
//从Token中获取userEmail(账户)
userEmail = jwtService.extractUsername(jwt);
//SecurityContextHolder 中的 Authentication 为空时,才进行处理
if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
//获取用户信息
UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
//从数据库中查询Token并判别Token状况是否正常
var isTokenValid = tokenRepository.findByToken(jwt)
.map(t -> !t.isExpired() && !t.isRevoked())
.orElse(false);
//假如Token有用,而且Token状况正常,将用户信息存储到SecurityContextHolder
if (jwtService.isTokenValid(jwt, userDetails) && isTokenValid) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails, //用户信息
null,
userDetails.getAuthorities() //用户的权限
);
authToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request) //拜访信息
);
//将用户信息以及权限保存到 SecurityContextHolder的上下文中,便利后续运用
//eg: 获取当时用户id,获取当时用户权限等等
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
}
}
上述代码首要逻辑为: 从恳求头中获取到token
.验证token
的有用性并解析token
中的信息存储到SecurityContextHolder
上下文中,便利后续的运用.
退出登录
登录
以及token
的校验现已说过了,现在就差一个退出登录了.咱们是否还记住咱们之前装备过一个退出登录
的恳求途径: /api/v1/auth/logout
.当咱们恳求恳求这个途径的时分,security
会帮咱们找到对应的LogoutHandler
,然后调用logout
办法完成退出登录.
@Service
@RequiredArgsConstructor
public class LogoutService implements LogoutHandler {
private final TokenRepository tokenRepository;
@Override
public void logout(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication
) {
//从恳求头中获取鉴权信息
final String authHeader = request.getHeader("Authorization");
final String jwt;
if (authHeader == null ||!authHeader.startsWith("Bearer ")) {
return;
}
//接续出token
jwt = authHeader.substring(7);
//从数据库中查询出token信息
var storedToken = tokenRepository.findByToken(jwt)
.orElse(null);
if (storedToken != null) {
//设置token过期
storedToken.setExpired(true);
storedToken.setRevoked(true);
tokenRepository.save(storedToken);
//铲除SecurityContextHolder上下文
SecurityContextHolder.clearContext();
}
}
}
security
帮咱们做了许多的工作,咱们只需求把token
置为失效状况,然后铲除掉SecurityContextHolder
上下文,就处理了悉数的问题
鉴权
下面经过几个例子,来讲解两种不同的鉴权装备办法
controller
@RestController
@RequestMapping("/api/v1/admin")
@PreAuthorize("hasRole('ADMIN')") //用户需求ADMIN人物才干拜访
public class AdminController {
@GetMapping
@PreAuthorize("hasAuthority('admin:read')") //用户需求admin:read权限才干拜访
public String get() {
return "GET:: admin controller";
}
@PostMapping
@PreAuthorize("hasAuthority('admin:create')") //用户需求admin:create权限才干拜访
@Hidden
public String post() {
return "POST:: admin controller";
}
@PutMapping
@PreAuthorize("hasAuthority('admin:update')")
@Hidden
public String put() {
return "PUT:: admin controller";
}
@DeleteMapping
@PreAuthorize("hasAuthority('admin:delete')")
@Hidden
public String delete() {
return "DELETE:: admin controller";
}
}
装备文件
下面贴出SecurityConfiguration
装备类的部分代码