XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>top.ss007</groupId>
<artifactId>spring-learn</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>top.ss007</groupId>
<artifactId>spring-security-mvc-inspect</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-security-mvc-inspect</name>
<description>spring-security-mvc-inspect</description>
<properties>
<java.version>11</java.version>
<hutool-jwt.version>5.8.11</hutool-jwt.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-jwt</artifactId>
<version>${hutool-jwt.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package top.ss007.springsecuritymvcinspect.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.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.EnableWebSecurity;
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.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import top.ss007.springsecuritymvcinspect.security.JwtAuthenticationProvider;
import top.ss007.springsecuritymvcinspect.security.JwtAuthenticationTokenFilter;
import top.ss007.springsecuritymvcinspect.security.MyAccessDeniedHandler;
import top.ss007.springsecuritymvcinspect.security.MyUnauthorizedHandler;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
@Autowired
private MyUnauthorizedHandler unauthorizedHandler;
@Autowired
private MyAccessDeniedHandler accessDeniedHandler;
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
return new JwtAuthenticationTokenFilter();
}
@Bean
public JwtAuthenticationProvider jwtAuthenticationProvider(){
return new JwtAuthenticationProvider();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
//由于使用的是JWT,这里不需要csrf防护
httpSecurity.csrf().disable()
//基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
//允许对于网站静态资源的无授权访问
.antMatchers(HttpMethod.GET,
"/",
"/*.html"
).permitAll()
//对登录注册允许匿名访问
.antMatchers("/user/login1", "/user/register").permitAll()
//跨域请求会先进行一次options请求
.antMatchers(HttpMethod.OPTIONS).permitAll()
//测试时全部运行访问.permitAll();
.antMatchers("/test/**").permitAll()
.anyRequest()// 除上面外的所有请求全部需要鉴权认证
.authenticated();
// 禁用缓存
httpSecurity.headers().cacheControl();
//使用自定义provider
httpSecurity.authenticationProvider(jwtAuthenticationProvider());
//添加JWT filter
httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//添加自定义未授权和未登录结果返回
httpSecurity.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(unauthorizedHandler);
return httpSecurity.build();
}
}
package top.ss007.springsecuritymvcinspect.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class ApiController {
@PreAuthorize("hasRole('admin')")
@GetMapping("/users/{id}")
public String getUserDetail(@PathVariable String id){
return "用户详情:" + id;
}
}
package top.ss007.springsecuritymvcinspect.controller;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.signers.JWTSignerUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.ss007.springsecuritymvcinspect.model.MyConstant;
import top.ss007.springsecuritymvcinspect.model.web.SignInReq;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class AuthController {
@Autowired
AuthenticationManager authenticationManager;
@PostMapping("/register")
public String register() {
return "注册成功";
}
@PostMapping("/login")
public String login(@RequestBody SignInReq req) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(req.getUsername(), req.getPassword());
authenticationManager.authenticate(authenticationToken);
String token = JWT.create()
.setExpiresAt(new Date(System.currentTimeMillis() + (1000 * 30)))
.setPayload("username", req.getUsername())
.setKey(MyConstant.JWT_SIGN_KEY.getBytes(StandardCharsets.UTF_8))
.sign();
return token;
}
}
package top.ss007.springsecuritymvcinspect.model.enumerate;
public enum RoleType {
ADMIN("admin"), USER("user");
private String roleName;
RoleType(String roleName) {
this.roleName = roleName;
}
public String getRoleName() {
return roleName;
}
}
package top.ss007.springsecuritymvcinspect.model.web;
import lombok.Data;
@Data
public class SignInReq {
private String username;
private String password;
}
package top.ss007.springsecuritymvcinspect.model;
public class MyConstant {
public static final String JWT_SIGN_KEY = "key";
}
package top.ss007.springsecuritymvcinspect.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import top.ss007.springsecuritymvcinspect.model.enumerate.RoleType;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
private RoleType roleType;
}
package top.ss007.springsecuritymvcinspect.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String userName;
private String password;
private List<Role> roles;
}
package top.ss007.springsecuritymvcinspect.security;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
public class JwtAuthenticationProvider implements AuthenticationProvider {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = String.valueOf(authentication.getPrincipal());
String password = String.valueOf(authentication.getCredentials());
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if(passwordEncoder.matches(password,userDetails.getPassword())){
return new UsernamePasswordAuthenticationToken(username, password, userDetails.getAuthorities());
}
throw new BadCredentialsException("Error!!");
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.equals(authentication);
}
}
package top.ss007.springsecuritymvcinspect.security;
import cn.hutool.jwt.Claims;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.JWTValidator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import top.ss007.springsecuritymvcinspect.model.MyConstant;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private final static String AUTH_HEADER = "Authorization";
private final static String AUTH_HEADER_TYPE = "Bearer";
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// get token from header: Authorization: Bearer <token>
String authHeader = request.getHeader(AUTH_HEADER);
if (Objects.isNull(authHeader) || !authHeader.startsWith(AUTH_HEADER_TYPE)){
filterChain.doFilter(request,response);
return;
}
String authToken = authHeader.split(" ")[1];
log.info("authToken:{}" , authToken);
//verify token
if (!JWTUtil.verify(authToken, MyConstant.JWT_SIGN_KEY.getBytes(StandardCharsets.UTF_8))) {
log.info("invalid token");
filterChain.doFilter(request,response);
return;
}
//JWTValidator.of(authToken).validateDate();
final String userName = (String) JWTUtil.parseToken(authToken).getPayload("username");
UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
package top.ss007.springsecuritymvcinspect.security;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 当访问接口没有权限时回调
*/
@Slf4j
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
log.error("access error", accessDeniedException);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println("禁止访问");
response.getWriter().flush();
}
}
package top.ss007.springsecuritymvcinspect.security;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 未认证时回调,也就是说没有登录
*/
@Slf4j
@Component
public class MyUnauthorizedHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
log.error("Unauthorized error", authException);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println("认证失败");
response.getWriter().flush();
}
}
package top.ss007.springsecuritymvcinspect.security;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import top.ss007.springsecuritymvcinspect.model.User;
import java.util.Collection;
import java.util.stream.Collectors;
/**
* spring security 需要
*/
@RequiredArgsConstructor
public class UserDetailsImpl implements UserDetails {
private final User user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getRoles()
.stream()
.map(role -> new SimpleGrantedAuthority(role.getRoleType().getRoleName()))
.collect(Collectors.toList());
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUserName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
package top.ss007.springsecuritymvcinspect.security;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import top.ss007.springsecuritymvcinspect.model.User;
import top.ss007.springsecuritymvcinspect.service.UserService;
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.getUserByName(username);
return new UserDetailsImpl(user);
}
}
package top.ss007.springsecuritymvcinspect.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import top.ss007.springsecuritymvcinspect.model.Role;
import top.ss007.springsecuritymvcinspect.model.User;
import top.ss007.springsecuritymvcinspect.model.enumerate.RoleType;
import top.ss007.springsecuritymvcinspect.service.UserService;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
PasswordEncoder passwordEncoder;
@Override
public User getUserByName(String userName) {
if (!"shusheng007".equals(userName)) {
throw new RuntimeException("用户不存在");
}
// List<Role> roles = List.of(new Role(RoleType.ADMIN), new Role(RoleType.USER));
List<Role> roles = List.of( new Role(RoleType.USER));
return new User(userName, passwordEncoder.encode("123456"), roles);
}
}
package top.ss007.springsecuritymvcinspect.service;
import top.ss007.springsecuritymvcinspect.model.User;
public interface UserService {
User getUserByName(String userName);
}