SpringSecurity + jwt安全认证模块使用心得

11 篇文章 0 订阅
3 篇文章 0 订阅

笔者之前的项目,并没有做token校验,只是做了最简单的登陆模块,对于一些请求也没有做拦截处理导致了服务器的日志中总是出现一些错误请求的日志,极大的降低了对任务日志查询的效率,所以近期将整个服务的安全模块加了上去,在这里记录一下。


一、添加springsecurity和jwt对应的依赖

<!-- spring security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- jwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

这里稍微拓展一下,说下jwt是个啥东西

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
说白了,这东西就是一个安全令牌生成工具,使用这个工具生成的令牌可以跨域使用,而且比你自己生成的token相对来说安全许多。

二、创建WebSecurityConfig配置文件(重要步骤)

代码如下:

package com.muyichen.demo.config;

import com.muyichen.demo.Filter.JwtAuthenticationFilter;
import com.muyichen.demo.Filter.JwtLoginFilter;
import com.muyichen.demo.security.JwtAuthenticationProvider;
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.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.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;

/**
 * 安全服务配置
 * @author muyichen
 * @date 2021-4-8
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(AuthenticationManagerBuilder auth) {
        // 使用自定义登录身份认证组件
        auth.authenticationProvider(new JwtAuthenticationProvider(userDetailsService));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 禁用 csrf, 由于使用的是JWT,我们这里不需要csrf
        http.cors().and().csrf().disable()
                .authorizeRequests()
                // 跨域预检请求
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                // 首页放行、登陆校验接口放行
                .antMatchers("/index", "/login").permitAll()
                // 暂时放行接口
                .antMatchers("/wb/**").permitAll()
                // 静态资源文件
                .antMatchers("/static/**").permitAll()
                // swagger
                .antMatchers("/swagger**/**").permitAll()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/v2/**").permitAll()
                // 其他所有请求需要身份认证
                .anyRequest().authenticated();
        // 退出登录处理器
        http.logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler());
        // 开启登录认证流程过滤器
        http.addFilterBefore(new JwtLoginFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
        // 访问控制时登录状态检查过滤器
        http.addFilterBefore(new JwtAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

}

配置文件中需要注意的几个点

  • 1、使用csrf().disable()将脚本校验的方法关了,因为使用jwt,所以可以关闭脚本校验来提升性能;
  • 2、如果存在跨域访问,需要使用.antMatchers(HttpMethod.OPTIONS, “/**”).permitAll()将跨域请求放行;
  • 3、如果并不是前后端分离项目的话,静态资源文件也需要放行,不然页面无法加载相应的样式;
  • 4、http.addFilterBefore这个方法是一定要加的,这个方法的意思是:在后面一个参数的方法执行之前先执行前一个参数所对应的方法,不加的话就没有办法进行jwt校验了;
  • 5、当然最最重要的是用户的登陆校验功能了,因为这个需要匹配上自身系统的用户信息,所以需要重写configure(AuthenticationManagerBuilder auth)方法来使用自定义的登陆校验组建;

三、由于第二步提到了跨域,所以这边也附上允许跨域的配置文件(不是必要)

package com.muyichen.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 跨域请求配置(允许跨域)
 * @author muyichen
 * @date 2021-4-8
 */
@Configuration
public class CorConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")    // 允许跨域访问的路径
                .allowedOrigins("*")    // 允许跨域访问的源
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")    // 允许请求方法
                .maxAge(168000)    // 预检间隔时间
                .allowedHeaders("*")  // 允许头部设置
                .allowCredentials(true);    // 是否发送cookie
    }
}

四、自建登陆校验模块(重要步骤)

1、创建登陆校验业务

该业务中需要添加上自身服务器的用户登陆校验功能,该业务实现了security中自带的UserDetailsService接口,所以在WebSecurityConfig配置文件中可以自动注入

package com.muyichen.deom.security;

import com.muyichen.demo.entity.UserInf;
import com.muyichen.demo.service.UserInfService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * 用户登录认证
 * @author muyichen
 * @date 2021-4-8
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserInfService userInfService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserInf userInf = userInfService.getUserInfByUserName(username);
        if (userInf == null) {
            throw new UsernameNotFoundException("该用户不存在");
        }
        // 用户权限列表,根据用户拥有的权限标识与如 @PreAuthorize("hasAuthority('sys:menu:view')") 标注的接口对比,决定是否可以调用接口
        Set<String> permissions = userInfService.findPermissions(username);
        List<GrantedAuthority> grantedAuthorities = permissions.stream().map(GrantedAuthorityImpl::new).collect(Collectors.toList());
        //加密密码
        String password = new BCryptPasswordEncoder().encode(userInf.getPassword());
        return new JwtUserDetails(username, password, grantedAuthorities);
    }
}

2、创建身份验证提供者

该身份提供者主要是用来向security模块中添加自定义的登陆校验业务用的,具体代码如下:

package com.muyichen.demo.security;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.bcrypt.BCryptPasswordEncoder;

/**
 * 身份验证提供者
 * @author muyichen
 * @date 2021-4-8
 */
public class JwtAuthenticationProvider extends DaoAuthenticationProvider {

    public JwtAuthenticationProvider(UserDetailsService userDetailsService) {
        //加载自定义的登陆校验业务
        super.setUserDetailsService(userDetailsService);
        //设置加密格式
        super.setPasswordEncoder(new BCryptPasswordEncoder());
    }

}

3、创建权限封装类

该类主要用来封装从后台传输过来的用户权限

package com.muyichen.demo.security;

import org.springframework.security.core.GrantedAuthority;

/**
 * 权限封装
 * @author muyichen
 * @date 2021-4-8
 */
public class GrantedAuthorityImpl implements GrantedAuthority {

    private static final long serialVersionUID = 1L;

    private String authority;

    public GrantedAuthorityImpl(String authority) {
        this.authority = authority;
    }

    public void setAuthority(String authority) {
        this.authority = authority;
    }

    @Override
    public String getAuthority() {
        return this.authority;
    }
}

登陆模块要注意的问题

1、在身份提供者中笔者设置了密码的加密方式,所以当前端传送密码过来的时候会先对密码进行相关的加密然后再和数据库中的密码进行对比校验,笔者由于之前存入的密码都没有加密过,所以在业务模块获取密码后给密码做了一次加密处理;
2、笔者这里的权限必须要有数据,没有的话会出问题;(权限可以随便写一串字符串,只要接口上没有加上对应的权限,就都能访问)

下面是JwtUserDetails的代码:

package com.muyichen.demo.security;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

/**
 * 安全用户模型
 * @author muyichen
 * @date 2021-4-8
 */
public class JwtUserDetails extends User {

    private static final long serialVersionUID = 1L;

    public JwtUserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        this(username, password, true, true, true, true, authorities);
    }

    public JwtUserDetails(String username, String password, boolean enabled, boolean accountNonExpired,
                          boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
    }

}

五、创建登陆校验过滤器(重要步骤)

该过滤器保证了每次登陆请求都会被拦截,并且在过滤中处理完部分业务逻辑(该过滤器只对/login请求生效,其他登陆请求无效),下面是具体代码:

package com.muyichen.demo.Filter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.muyichen.demo.security.JwtAuthenticationToken;
import com.muyichen.demo.util.HttpUtil;
import com.muyichen.demo.util.JwtTokenUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
 * 启动登录认证流程过滤器(该方法默认是对post方式的/login请求生效,详情请看父类)
 * @author muyichen
 * @date 2021-4-8
 */
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {

    public JwtLoginFilter(AuthenticationManager authManager) {
        setAuthenticationManager(authManager);
    }

	/**
     * 处理部分参数逻辑
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        // 可以在此覆写尝试进行登录认证的逻辑,登录成功之后等操作不再此方法内
        // 如果使用此过滤器来触发登录认证流程,注意登录请求数据格式的问题
        // 此过滤器的用户名密码默认从request.getParameter()获取,但是这种
        // 读取方式不能读取到如 application/json 等 post 请求数据,需要把
        // 用户名密码的读取逻辑修改为到流中读取request.getInputStream()

        // 对于使用@RequestBody注解来接收参数
//        String body = getBody(request);
//        JSONObject jsonObject = JSON.parseObject(body);
//        String username = jsonObject.getString("username") == null ? "":request.getParameter("username");
//        String password = jsonObject.getString("password") == null ? "":request.getParameter("password");

        // 对于使用@RequestParam注解来接收参数
        String username = request.getParameter("username") == null ? "":request.getParameter("username");
        String password = request.getParameter("password") == null ? "":request.getParameter("password");
        username = username.trim();
        JwtAuthenticationToken authRequest = new JwtAuthenticationToken(username, password);
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);

    }

	/**
     * 校验成功后将生成的token返回给前端页面
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication authResult) throws IOException {
        // 存储登录认证信息到上下文
        SecurityContextHolder.getContext().setAuthentication(authResult);
        // 记住我服务
        getRememberMeServices().loginSuccess(request, response, authResult);
        // 触发事件监听器
        if (this.eventPublisher != null) {
            eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
        }
        // 生成并返回token给客户端,后续访问携带此token
        JwtAuthenticationToken token = new JwtAuthenticationToken(null, null, JwtTokenUtils.generateToken(authResult));
        HttpUtil.write(response, token);
    }

    /** 
     * 获取请求Body
     * @param request
     * @return
     */
    public String getBody(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}

上述代码中使用到了一个JwtAuthenticationToken令牌对象实体类,代码如下:

package com.muyichen.demo.security;

import java.util.Collection;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

/**
 * 自定义令牌对象
 * @author xuyang
 * @date 2021-4-8
 */
public class JwtAuthenticationToken extends UsernamePasswordAuthenticationToken {

    private static final long serialVersionUID = 1L;

    private String token;

    public JwtAuthenticationToken(Object principal, Object credentials){
        super(principal, credentials);
    }

    public JwtAuthenticationToken(Object principal, Object credentials, String token){
        super(principal, credentials);
        this.token = token;
    }

    public JwtAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities, String token) {
        super(principal, credentials, authorities);
        this.token = token;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public static long getSerialversionuid() {
        return serialVersionUID;
    }

}

还有一点,写了这个过滤器后,登陆接口可以不做具体的实现,如下方代码:

/**
 * 登录接口(通过自定义的拦截器,拦截该请求进行校验)
 */
@PostMapping("/login")
public void login(@RequestParam String username, @RequestParam String password) {}

注:代码中的JwtTokenUtils工具类和HttpUtil工具类在后文补上

六:创建jwt令牌校验过滤器(重要步骤)

该过滤器主要是用来做token校验的,凡是在WebSecurityConfig配置文件中没有放行的接口都需要通过这个token校验才能放行。代码如下:

package com.muyichen.demo.Filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.muyichen.demo.util.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

/**
 * 登录认证检查过滤器
 * @author muyichen
 * @date 2021-4-8
 */
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {

    @Autowired
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 获取token, 并检查登录状态
        SecurityUtils.checkAuthentication(request);
        chain.doFilter(request, response);
    }

}

注:代码中的SecurityUtils工具类在后文补上

完成上述六步后,一个包含登陆校验、token认证、请求拦截的SpringSecurity + jwt安全认证模块就完成了,当时相对于shrio它使用起来还是比较复杂的。

补上之前欠缺的三个工具类

1、JwtTokenUtils工具类

package com.muyichen.demo.util;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.muyichen.demo.security.GrantedAuthorityImpl;
import com.muyichen.demo.security.JwtAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
 * JWT工具类
 * @author muyichen
 * @date 2021-4-8
 */
public class JwtTokenUtils implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 用户名称
     */
    private static final String USERNAME = Claims.SUBJECT;
    /**
     * 创建时间
     */
    private static final String CREATED = "created";
    /**
     * 权限列表
     */
    private static final String AUTHORITIES = "authorities";
    /**
     * 密钥
     */
    private static final String SECRET = "abcdefgh";
    /**
     * 有效期12小时
     */
    private static final long EXPIRE_TIME = 12 * 60 * 60 * 1000;

    /**
     * 生成令牌
     *
     * @param userDetails 用户
     * @return 令牌
     */
    public static String generateToken(Authentication authentication) {
        Map<String, Object> claims = new HashMap<>(3);
        claims.put(USERNAME, SecurityUtils.getUsername(authentication));
        claims.put(CREATED, new Date());
        claims.put(AUTHORITIES, authentication.getAuthorities());
        return generateToken(claims);
    }

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private static String generateToken(Map<String, Object> claims) {
        Date expirationDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, SECRET).compact();
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public static String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    /**
     * 根据请求令牌获取登录认证信息
     * @param token 令牌
     * @return 用户名
     */
    public static Authentication getAuthenticationeFromToken(HttpServletRequest request) {
        Authentication authentication = null;
        // 获取请求携带的令牌
        String token = JwtTokenUtils.getToken(request);
        if(token != null) {
            // 请求令牌不能为空
            if(SecurityUtils.getAuthentication() == null) {
                // 上下文中Authentication为空
                Claims claims = getClaimsFromToken(token);
                if(claims == null) {
                    return null;
                }
                String username = claims.getSubject();
                if(username == null) {
                    return null;
                }
                if(isTokenExpired(token)) {
                    return null;
                }
                Object authors = claims.get(AUTHORITIES);
                List<GrantedAuthority> authorities = new ArrayList<>();
                if (authors != null && authors instanceof List) {
                    for (Object object : (List) authors) {
                        authorities.add(new GrantedAuthorityImpl((String) ((Map) object).get("authority")));
                    }
                }
                authentication = new JwtAuthenticationToken(username, null, authorities, token);
            } else {
                if(validateToken(token, SecurityUtils.getUsername())) {
                    // 如果上下文中Authentication非空,且请求令牌合法,直接返回当前登录认证信息
                    authentication = SecurityUtils.getAuthentication();
                }
            }
        }
        return authentication;
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private static Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    /**
     * 验证令牌
     * @param token
     * @param username
     * @return
     */
    public static Boolean validateToken(String token, String username) {
        String userName = getUsernameFromToken(token);
        return (userName.equals(username) && !isTokenExpired(token));
    }

    /**
     * 刷新令牌
     * @param token
     * @return
     */
    public static String refreshToken(String token) {
        String refreshedToken;
        try {
            Claims claims = getClaimsFromToken(token);
            claims.put(CREATED, new Date());
            refreshedToken = generateToken(claims);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }

    /**
     * 判断令牌是否过期
     *
     * @param token 令牌
     * @return 是否过期
     */
    public static Boolean isTokenExpired(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取请求token
     * @param request
     * @return
     */
    public static String getToken(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        String tokenHead = "Bearer ";
        if(token == null) {
            token = request.getHeader("token");
        } else if(token.contains(tokenHead)){
            token = token.substring(tokenHead.length());
        }
        if("".equals(token)) {
            token = null;
        }
        return token;
    }

}

2、HttpUtil工具类

package com.muyichen.demo.util;

import com.muyichen.demo.response.BaseResult;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

/**
 * 登录认证检查过滤器
 * @author muyichen
 * @date 2021-4-8
 */
@Slf4j
public class HttpUtil {

    /**
     * 输出信息到浏览器
     * @param response
     * @throws IOException
     */
    public static void write(HttpServletResponse response, Object data) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        BaseResult<Object> result = new BaseResult<>("", data);
        String json = JSONObject.toJSONString(result);
        response.getWriter().print(json);
        response.getWriter().flush();
        response.getWriter().close();
    }

}

这里的响应对象可以用自己的,不必非要纠结用BaseResult

3、SecurityUtils工具类

package com.muyichen.demo.util;


import com.muyichen.demo.security.JwtAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;

import javax.servlet.http.HttpServletRequest;

/**
 * Security相关操作
 * @author muyichen
 * @date 2021-4-8
 */
public class SecurityUtils {

    /**
     * 系统登录认证
     * @param request
     * @param username
     * @param password
     * @param authenticationManager
     * @return
     */
    public static JwtAuthenticationToken login(HttpServletRequest request, String username, String password, AuthenticationManager authenticationManager) {
        JwtAuthenticationToken token = new JwtAuthenticationToken(username, password);
        token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        // 执行登录认证过程
        Authentication authentication = authenticationManager.authenticate(token);
        // 认证成功存储认证信息到上下文
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌并返回给客户端
        token.setToken(JwtTokenUtils.generateToken(authentication));
        return token;
    }

    /**
     * 获取令牌进行认证
     * @param request
     */
    public static void checkAuthentication(HttpServletRequest request) {
        // 获取令牌并根据令牌获取登录认证信息
        Authentication authentication = JwtTokenUtils.getAuthenticationeFromToken(request);
        // 设置登录认证信息到上下文
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    /**
     * 获取当前用户名
     * @return
     */
    public static String getUsername() {
        String username = null;
        Authentication authentication = getAuthentication();
        if(authentication != null) {
            Object principal = authentication.getPrincipal();
            if(principal != null && principal instanceof UserDetails) {
                username = ((UserDetails) principal).getUsername();
            }
        }
        return username;
    }

    /**
     * 获取用户名
     * @return
     */
    public static String getUsername(Authentication authentication) {
        String username = null;
        if(authentication != null) {
            Object principal = authentication.getPrincipal();
            if(principal != null && principal instanceof UserDetails) {
                username = ((UserDetails) principal).getUsername();
            }
        }
        return username;
    }

    /**
     * 获取当前登录信息
     * @return
     */
    public static Authentication getAuthentication() {
        if(SecurityContextHolder.getContext() == null) {
            return null;
        }
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication;
    }

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Spring SecurityJWT(JSON Web Token)是一种常见的组合,用于实现身份验证和授权机制。 JWT是一种轻量级的身份验证和授权的解决方案,它使用JSON格式来定义安全声明。JWT通常由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。头部包含算法和令牌类型等信息,载荷包含用户身份和其他相关信息,签名用于验证令牌的完整性和真实性。 Spring Security提供了对JWT的支持,并可以与Spring Boot框架无缝集成。您可以通过以下步骤来实现Spring Security + JWT的集成: 1. 添加依赖:在项目的构建文件,添加Spring SecurityJWT相关的依赖,如spring-boot-starter-security和jjwt。 2. 配置Spring Security:创建一个继承自WebSecurityConfigurerAdapter的配置类,并重写configure方法来配置Spring Security。在该方法,您可以定义身份验证和授权规则。 3. 创建JWT工具类:创建一个JWT工具类,用于生成、解析和验证JWT。您可以使用jjwt库来处理JWT操作。 4. 实现用户认证:在Spring Security配置类,您可以实现UserDetailsService接口,并重写loadUserByUsername方法来根据用户名加载用户信息。在该方法,您可以从数据库或其他数据源获取用户信息,并构建一个UserDetails对象返回。 5. 实现JWT过滤器:创建一个自定义的过滤器,用于解析和验证传入请求JWT。在该过滤器,您可以使用JWT工具类来解析JWT,并将用户信息添加到Spring Security的上下文。 6. 配置Spring Security过滤器链:在Spring Security配置类,将JWT过滤器添加到过滤器,以确保每个请求都经过JWT验证。 通过以上步骤,您可以实现Spring SecurityJWT的集成,实现基于JWT的身份验证和授权机制。这样,您可以使用JWT生成和验证令牌,并通过Spring Security保护您的应用程序资源。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

缘丶沐逸尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值