SpringBoot整合SpringSecurity
一、添加依赖
<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>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
二、添加配置文件
2.1、过滤器配置
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final PathMatcher pathmatcher = new AntPathMatcher();
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
for (String reg : NoneAuthedResources.BACKEND_RESOURCES) {
if (pathmatcher.match(reg, request.getServletPath())) {
chain.doFilter(request, response);
return;
}
}
try {
Map<String, Object> claims = JwtUtils.validateTokenAndGetClaims(request);
String role = String.valueOf(claims.get(ROLE));
Long userId = Long.valueOf(claims.get("userId") + "");
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(userId, null, Arrays.asList(() -> ""))
);
} catch (Exception e) {
ResponseUtils.buildResponse(response, R.response(ResultCodeEnum.NOT_AUTHENTICATION,null), HttpStatus.UNAUTHORIZED);
return;
}
chain.doFilter(request, response);
}
}
2.2、跨域配置
跨域支持
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 跨域支持
*
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.allowedHeaders("*")
.maxAge(3600 * 24);
}
}
2.3、Security配置类
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
return firewall;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.cors() //允许跨域访问
.and()
.authorizeRequests()
// 配置那些url需要进行校验--所有请求都需要校验"/"
.antMatchers("/").authenticated()
//那些请求不需要校验
.antMatchers(NoneAuthedResources.BACKEND_RESOURCES).permitAll()
.anyRequest().authenticated() //自定义校验类
.and()
.addFilterBefore(new JwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
//关闭session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(NoneAuthedResources.FRONTEND_RESOURCES);
}
}
2.4、白名单资源类
/**
* 不需要身份验证的资源
*
* @author fengshuonan
* @Date 2020/3/1 16:19
*/
public class NoneAuthedResources {
/**
* 前端接口资源
*/
public static final String[] FRONTEND_RESOURCES = {
// Swagger相关资源
"/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html/**",
"/webjars/**",
"/druid/**",
};
/**
* 不要权限校验的后端接口资源
* <p>
* ANT风格的接口正则表达式:
* <p>
* ? 匹配任何单字符<br/>
* * 匹配0或者任意数量的 字符<br/>
* ** 匹配0或者更多的 目录<br/>
*/
public static final String[] BACKEND_RESOURCES = {
// 登录相关接口放开过滤
"/login-api/login",
// 上传图片接口
"/commonController/pictureUpload",
// Swagger及druid相关资源
"/swagger**/**",
"/webjars/**",
"/v2/api-docs",
"/druid",
};
}
2.5、获取当前登录人配置类
@Slf4j
@Service
public class SecurityUser{
@Autowired
private UserMapper userMapper;
public Long getLoginId() {
Object userId = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (ObjectUtils.isEmpty(userId)){
throw new RuntimeException("获取登录人失败");
}
return Long.valueOf(userId + "");
}
/**
* 获取当前登录人
*
* @return
*/
public User getLoginUser() {
User user = userMapper.selectById(getLoginId());
return user;
}
}
三、添加工具类
3.1、JWT工具类
public class JwtUtils {
/**
* 令牌环有效期
*/
public static final long EXPIRATION_TIME = 48 * 60 * 60 * 1000;
/**
* 令牌环密钥
*/
public static final String SECRET = "qrAVEd537GKHcxHW";
/**
* 令牌环头标识
*/
public static final String TOKEN_PREFIX = "Bearer";
/**
* 配置令牌环在http heads中的键值
*/
public static final String HEADER_STRING = "Authorization";
/**
* 自定义字段-角色字段
*/
public static final String ROLE = "ROLE";
//生成令牌环
public static String generateToken(String userRole, Long 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))
// 颁发时间
.setIssuedAt(new Date())
// 加密算法和密钥
.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 (Objects.isNull(token) || StringUtils.equals(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.2、http相应工具类
public class ResponseUtils {
public static void buildResponse(HttpServletResponse response, Object result, HttpStatus httpStatus) throws IOException {
// 返回JSON
response.setContentType("application/json;charset=utf-8");
// 状态码
response.setStatus(httpStatus.value());
response.getWriter().write(JSONUtil.toJsonStr(result));
}
}
四、编写控制器测试
@RequestMapping("/login-api")
public class LoginController {
@Autowired private UserService userService;
@Autowired private WebSecurityConfig webSecurityConfig;
@Autowired private SecurityUser securityUser;
@ApiOperation("登录")
@PostMapping("/login")
public R save(@RequestBody User user) {
public R login(@RequestBody User user) {
User userInDb = userService.getOne(new QueryWrapper<User>().eq("username", user.getUsername()));
if (ObjectUtils.isEmpty(userInDb)){
return R.fail("账号不存在");
}
return R.success();
String encode = webSecurityConfig.passwordEncoder().encode("123456");
boolean isMatches = webSecurityConfig.passwordEncoder().matches(user.getPassword(), userInDb.getPassword());
if (!isMatches){
return R.fail("账号或密码错误");
}
String token = JwtUtils.generateToken("admin", userInDb.getUserId());
return R.success(
"登陆成功",
new HashMap<String, Object>(){{
put(JwtUtils.HEADER_STRING, token);
put("username", userInDb.getUsername());
put("roles", "admin");
}}
);
}
@ApiOperation("用户信息")
@PostMapping("/userinfo")
public R userinfo() {
return R.success(securityUser.getLoginUser());
}
}