spring boot整合SpringSecurity-04 使用jwt的方式认证

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()));
        }
    }

}

有什么问题可以评论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值