spring boot整合SpringSecurity 目录
spring boot整合SpringSecurity-01入门
spring boot整合SpringSecurity-02 基于Serssion的认证
spring boot整合SpringSecurity-03 自定义报错信息
spring boot整合SpringSecurity-04 使用jwt的方式认证
使用jwt的方式认证
配置
import com.jiang.login.security.filter.JWTAuthenticationFilter;
import com.jiang.login.security.filter.JWTAuthorizationFilter;
import com.jiang.login.security.handler.JWTAccessDeniedHandler;
import com.jiang.login.security.handler.JWTAuthenticationEntryPoint;
import com.jiang.login.security.handler.JwtLogoutSuccessHandler;
import com.jiang.login.service.PermissionService;
import com.jiang.login.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
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.EnableWebSecurity;
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.web.authentication.www.BasicAuthenticationFilter;
/**
* JwtSecurityConfig
* 使用Jwt的方式进行认证授权
* @创建人 江枫沐雪
* @创建时间 2021/7/29 15:04
*/
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private PermissionService permissionService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//查询用户信息
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//用户详情信息
auth.userDetailsService(userDetailsService)
//密码加密方式
.passwordEncoder(bCryptPasswordEncoder);
}
//认证授权机制
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
// 配置跨域请求 并且 关闭打开的csrf保护
http.cors().and().csrf().disable()
// 认证配置
.authorizeRequests()
// 登录验证等放行
.antMatchers("/auth/**","/app/**").permitAll()
// 剩下的接口都需要登陆后访问
.anyRequest().authenticated()
.and()
添加登录过滤器
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
添加权限过滤器
.addFilterBefore(new JWTAuthorizationFilter(permissionService), BasicAuthenticationFilter.class)
// 因为使用的jwt认证方式,所有关闭session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.logout()
.logoutUrl("/logout")
// 注销成功处理程序
.logoutSuccessHandler(new JwtLogoutSuccessHandler())
.and()
// 异常处理
.exceptionHandling()
// 没有登录,返回
.authenticationEntryPoint(new JWTAuthenticationEntryPoint())
// 添加无权限时的处理
.accessDeniedHandler(new JWTAccessDeniedHandler());
}
}
登录过滤器
import com.hnbd.jinshui.pojo.JwtUser;
import com.hnbd.jinshui.pojo.LoginUser;
import com.hnbd.jinshui.pojo.response.ApiResponse;
import com.hnbd.jinshui.pojo.response.ApiResponseStatus;
import com.hnbd.jinshui.utils.JSONConverter;
import com.hnbd.jinshui.utils.JwtTokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* LoginAuthenticationFilter
* 登录验证过滤器
* @创建人 江枫沐雪
* @创建时间 2021/8/2 8:46
*/
@Slf4j
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
/**
* 参数构造
* @param authenticationManager 身份验证管理器
*/
public JWTAuthenticationFilter(AuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
super.setFilterProcessesUrl("/auth/login");
}
/**
* 验证用户身份
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
try{
//读取传递的数据信息
String username = request.getParameter("username");
String password = request.getParameter("password");
String rememberMe = request.getParameter("rememberMe");
LoginUser loginUser = new LoginUser();
loginUser.setPassword(password);
loginUser.setRememberMe(StringUtils.isBlank(rememberMe) ? 0 : Integer.parseInt(rememberMe));
loginUser.setUsername(username);
//开启认证
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginUser.getUsername(),loginUser.getPassword()));
}catch(Exception e){
log.error("原因:{}",e.getMessage());
throw new RuntimeException("用户名密码错误");
}
}
/**
* 登录验证成功
*/
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain
, Authentication auth) throws IOException {
//登录成功之后读取数据
JwtUser jwtUser = (JwtUser) auth.getPrincipal();
// 用户禁用判断
if (jwtUser.isInvalid()) {
//禁用 返回用户被禁用
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().append(JSONConverter.toJSON(ApiResponse.ofStatus(ApiResponseStatus.USER_INVALID)));
return;
}
//equals 是否相等 即是否记住密码
boolean isRemember = "1".equals(request.getParameter("rememberMe"));
String token = JwtTokenUtils.createToken(jwtUser, isRemember);
//将token 放回
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setHeader("X-Access-Token", token);
}
/**
* 验证失败
*/
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response
, AuthenticationException failed) throws IOException {
log.info("login error:{}", failed.getMessage());
// 设置字符编码 UTF-8
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
// 设置返回的内容类型 json类型
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// api 的相应状态
ApiResponseStatus status;
try {
//判断是否 身份验证服务异常
if (failed instanceof AuthenticationServiceException) {
status = ApiResponseStatus.INTERNAL_SERVER_ERROR;
} else {
status = ApiResponseStatus.PASSWORD_ERROR;
}
//返回结果
response.getWriter().append(JSONConverter.toJSON(ApiResponse.ofStatus(status)));
} catch (Exception e) {
log.error("无法写入身份验证响应", e);
}
}
}
权限过滤器
import com.hnbd.jinshui.pojo.JwtUser;
import com.hnbd.jinshui.pojo.response.ApiResponse;
import com.hnbd.jinshui.pojo.response.ApiResponseStatus;
import com.hnbd.jinshui.service.PermissionService;
import com.hnbd.jinshui.utils.JSONConverter;
import com.hnbd.jinshui.utils.JwtTokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.security.auth.login.AccountLockedException;
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;
/**
* JWTAuthorizationFilter
* OncePerRequestFilter 能够确保在一次请求只通过一次filter,而不需要重复执行
* @创建人 江枫沐雪
* @创建时间 2021/8/2 10:45
*/
@Slf4j
public class JWTAuthorizationFilter extends OncePerRequestFilter {
public PermissionService permissionService;
public JWTAuthorizationFilter( PermissionService permissionService) {
this.permissionService = permissionService;
}
/**
* 权限过滤器的设置
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String token = JwtTokenUtils.getRawToken(request);
// 如果请求中没有Authorization信息则直接放行了
if (token == null) {
chain.doFilter(request, response);
return;
}
// 如果请求头中有token,则进行解析,并且设置认证信息
// 解析token并将用户信息附加到Authentication中
try {
SecurityContextHolder.getContext().setAuthentication(getAuthentication(token));
} catch (TokenIsInvalidException e) {
// 返回json形式的错误信息
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
//没有登录
response.getWriter().write(JSONConverter.toJSON(ApiResponse.ofStatus(ApiResponseStatus.INVALID_TOKEN)));
response.getWriter().flush();
return;
} catch (AccountLockedException e) {
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
//用户已经被禁用
response.getWriter().write(JSONConverter.toJSON(ApiResponse.ofStatus(ApiResponseStatus.USER_INVALID)));
response.getWriter().flush();
}
//放行
chain.doFilter(request, response);
}
/**
* 这里从token中获取用户信息并新建一个token
* @param token 值
* @return 结果
*/
private UsernamePasswordAuthenticationToken getAuthentication(String token) throws TokenIsInvalidException, AccountLockedException {
//是否过期
boolean expiration = JwtTokenUtils.isExpiration(token);
if (expiration) {
//过期
throw new TokenIsInvalidException("token timeout");
} else {
JwtUser jwtUser = null;
try {
//读取token中的数据信息
jwtUser = JwtTokenUtils.getJwtUser(token);
} catch (Exception e) {
e.printStackTrace();
log.error("token 读取数据失败!{}",e.getMessage());
}
//如果数据不为空 但是被禁用
if (jwtUser != null && jwtUser.isInvalid()) {
throw new AccountLockedException(String.format("user: %s is invalid", jwtUser.getName()));
}
return jwtUser == null ? null
: new UsernamePasswordAuthenticationToken(jwtUser, null,
permissionService.getAuthoritiesByRoleIds(jwtUser.getRoleIds()));
}
}
}
有什么问题可以评论。