Spring Security前后分离校验token的实现方法

目录
  • 前言
  • token配置
    • 引入JWT依赖文件
    • 配置token管理器类
  • security配置
    • 配置未登录处理类
    • 配置无权限处理类
    • 配置登出操作处理类
    • 配置token认证过滤器
    • 配置token权限校验过滤器
    • 自定义加密类
    • 配置UserDetailService
    • 配置数据库User对象映射类
    • 配置UserDetailService使用的SecurityUser类
    • 配置mybatis-plus
    • 配置security配置类
    • 配置几个测试接口
    • Md5加密工具类
  • 测试
    • 首先测试登录
    • 测试存在权限的接口
    • 测试不存在权限的接口
    • 测试登出
    • 测试不需要权限的接口
  • 数据库sql脚本
  • 代码下载

前言

之前采取项目中嵌套html页面,实现基本的登录校验权限校验登出操作记住我等功能试下。

但是,现在的开发基本都是前后分离样式,后端并不需要配置登录页的操作。

如何才能做到前后分离,同时也能支持登录token校验呢,本篇博客详细说明。

token配置

本次token生成采取jwt的方式。

引入JWT依赖文件

<dependency>
  <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

配置token管理器类

自定一个Token生成和从token中解析用户名的一个类,并交给Spring管理。

package security.config;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.stereotype.Component;
import java.util.Calendar;
import java.util.HashMap;
@Component
public class TokenJwtManager {
    // 设置token时间
    private int tokenEcpiration = 24*60*60*1000; // 毫秒   24h
    // 编码密钥
    private String tokenSignKey = "123456";
    // 1、根据用户名生成token
    public String createToken(String userName){
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND, tokenEcpiration);
        String userName1 = JWT.create()
                .withHeader(new HashMap<>())
                .withClaim("userName", userName)
                .withExpiresAt(calendar.getTime()) // 过期时间
                .sign(Algorithm.HMAC256(tokenSignKey));// 签名
        return userName1;
    }
    // 2、根据token得到用户名信息
    public String getUserName(String token){
        JWTVerifier build = JWT.require(Algorithm.HMAC256(tokenSignKey)).build();
        DecodedJWT verify = build.verify(token);
        Claim userName = verify.getClaim("userName");
        return userName.asString();
    public static void main(String[] args) {
        String ss = new TokenJwtManager().createToken("1111111");
        System.out.println(ss);
        System.out.println(new TokenJwtManager().getUserName(ss));
}

security 配置

配置未登录处理类

package security.config.handler;

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;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
 * 未登录
 */
@Component
@Slf4j
public class MyUnAuthEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        log.info("======= commence ===");
        // 返回请求端
        Map<String,Object> resultMap = new HashMap<>();
        // 保存数据
        resultMap.put("code","10000");
        resultMap.put("msg","当前账户未登录");
        resultMap.put("data",new HashMap<>());
        // 设置返回消息类型
        httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        // 返回给请求端
        PrintWriter writer  = httpServletResponse.getWriter();
        writer.write(resultMap.toString());
        writer.close();
    }
}

配置无权限处理类

package security.config.handler;

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;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
 * 无权访问配置(前后分离)
 */
@Component  // 交给spring管理
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        Map<String,Object> resultMap = new HashMap<>();
        // 保存数据
        resultMap.put("code","403");
        resultMap.put("msg","无权访问");
        resultMap.put("data",null);
        // 设置返回消息类型
        httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        // 返回给请求端
        PrintWriter writer = httpServletResponse.getWriter();
        writer.write(resultMap.toString());
        writer.flush();
        writer.close();
    }
}

配置登出操作处理类

package security.config.handler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.stereotype.Component;
import security.config.TokenJwtManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
 * 登出
 */
@Component
@Slf4j
public class MyLogoutHandler implements LogoutHandler {
    @Autowired
    private TokenJwtManager tokenJwtManager;
//    public MyLogoutHandler(TokenJwtManager tokenJwtManager) {
//        this.tokenJwtManager = tokenJwtManager;
//    }
    @Override
    public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
        // 1、从header中获取token
        String token = httpServletRequest.getHeader("token");
        log.info("token信息为  {}",token);
        String userName = tokenJwtManager.getUserName(token);
        log.info("从token获取userName信息为  {}",token);
        // redis 移除登录信息等逻辑
        // xxxxx
        // 2、返回请求端
        Map<String,Object> resultMap = new HashMap<>();
        // 保存数据
        resultMap.put("code","200");
        resultMap.put("msg",userName+"登录成功");
        resultMap.put("data",new HashMap<>());
        // 设置返回消息类型
        httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        // 返回给请求端
        PrintWriter writer = null;
        try {
            writer = httpServletResponse.getWriter();
            writer.write(resultMap.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
           e.printStackTrace();
        }
    }
}

配置token认证过滤器

package security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
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 org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import security.config.TokenJwtManager;
import security.vo.SecurityUser;
import security.vo.User;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
// 这里交给spring管理会报错
@Slf4j
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
    private TokenJwtManager tokenJwtManager;
    private AuthenticationManager authenticationManager;
    public TokenLoginFilter(TokenJwtManager tokenJwtManager, AuthenticationManager authenticationManager) {
        this.tokenJwtManager = tokenJwtManager;
        this.authenticationManager = authenticationManager;
        this.setPostOnly(false); // 关闭登录只允许 post
        // 设置登陆路径,并且post请求
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/user/login","POST"));
    }
    // 1、获取登录页传递来的账户和密码信息
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){
        log.info("==== attemptAuthentication  ======");
        String userName = request.getParameter("userName");
        String pwd = request.getParameter("passWord");
        log.info("userName:{},pwd:{}",userName,pwd);
        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userName,
                pwd,new ArrayList<>()));
    // 2、认证成功调用
    @Autowired
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
            throws IOException, ServletException {
        log.info("==== successfulAuthentication  ======");
        // 认证成功之后,获取认证后的用户基本信息
        SecurityUser securityUser = (SecurityUser) authResult.getPrincipal();
        // 根据用户名生成对应的token
        String token = tokenJwtManager.createToken(securityUser.getUsername());
        // token信息存于redis、数据库、缓存等
        // 返回成功
        Map<String,Object> resultMap = new HashMap<>();
        // 保存数据
        resultMap.put("code","200");
        resultMap.put("msg","登录成功");
        resultMap.put("data",token);
        // 设置返回消息类型
        response.setHeader("Content-type", "text/html;charset=UTF-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=UTF-8");
        // 返回给请求端
        PrintWriter writer = response.getWriter();
        writer.write(resultMap.toString());
        writer.flush();
        writer.close();
    // 3、认证失败调用的方法
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)
        log.info("==== unsuccessfulAuthentication  ======");
        resultMap.put("code","500");
        resultMap.put("msg","登录验证失败");
        resultMap.put("data",new HashMap<>());
}

配置token权限校验过滤器

package security.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.stereotype.Component;
import security.config.TokenJwtManager;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
 * token 校验
 */
@Slf4j
//@Component // 交给 spring 会报错
public class TokenAuthFilter extends BasicAuthenticationFilter {
    private TokenJwtManager tokenJwtManager;
    public TokenAuthFilter(AuthenticationManager authenticationManager, TokenJwtManager tokenJwtManager) {
        super(authenticationManager);
        this.tokenJwtManager = tokenJwtManager;
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("====  doFilterInternal   ==========   token校验");
        //获取当前认证成功用户权限信息
        UsernamePasswordAuthenticationToken authRequest = getAuthentication(request);
        if(authRequest != null){
            // 有权限,则放入权限上下文中
            SecurityContextHolder.getContext().setAuthentication(authRequest);
        }
        // 执行下一个 filter 过滤器链
        chain.doFilter(request,response);
    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        log.info("==== getAuthentication =====");
        //从header获取token
        String token = request.getHeader("token");
        log.info("token:{}",token);
        if(token != null) {
            //从token获取用户名
            String username = tokenJwtManager.getUserName(token);
            log.info("解析token获取userName为:{}",username);
            // 数据库获取权限信息
            // 本次模拟
            List<String> permissionValueList = Arrays.asList("admin","select");
            Collection<GrantedAuthority> authority = new ArrayList<>();
            for(String permissionValue : permissionValueList) {
                SimpleGrantedAuthority auth = new SimpleGrantedAuthority(permissionValue);
                authority.add(auth);
            }
            return new UsernamePasswordAuthenticationToken(username,token,authority);
        return null;
}

自定义加密类

package security.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import security.utils.Md5Utils;
/**
 * 密码加密、比对
 */
@Component // bean
@Slf4j
public class DefaultPwdEndoder implements PasswordEncoder {
    /**
     * 加密
     * @param charSequence
     * @return
     */
    @Override
    public String encode(CharSequence charSequence) {
        log.info("==== encode ====");
        log.info("charSequence 为 {}",charSequence);
        log.info("charSequence md5为 {}",Md5Utils.md5(charSequence.toString()));
        return Md5Utils.md5(charSequence.toString());
    }
     * 进行密码比对
     * @param charSequence 不加密
     * @param encodePwd  加密
    public boolean matches(CharSequence charSequence, String encodePwd) {
        log.info("==== matches ====");
        log.info("charSequence:{}",charSequence);
        log.info("charSequenceMd5:{}",Md5Utils.md5(charSequence.toString()));
        log.info("encodePwd:{}",encodePwd);
        return encodePwd.equalsIgnoreCase(Md5Utils.md5(charSequence.toString()));
}

配置UserDetailService

package security.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.Component;
import org.springframework.stereotype.Service;
import security.mapper.UserMapper;
import security.vo.SecurityUser;
import security.vo.User;
import java.util.Arrays;
import java.util.List;
/**
 * security 登录信息和权限获取类
 */
@Service("userDetailsService")
@Slf4j
public class UserDetailService implements UserDetailsService {
    // 注入Usermapper
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        log.info("====== loadUserByUsername ======");
        // 通过username查询数据库获取用户信息
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("username",userName);
        User user = userMapper.selectOne(userQueryWrapper);
        // 判断用户是否存在
        if(user == null){
            throw new UsernameNotFoundException("账户信息不存在!");
        }
        // 存在对应的用户信息,则将其封装,丢给security自己去解析
        log.info("user:{}",user);
        // 权限暂时不查数据库
        List<String> admin = Arrays.asList("ROLE_user,ROLE_admin,admin");
        // 将数据封装给 SecurityUser ,因为 SecurityUser 是 UserDetails 的子类
        SecurityUser securityUser = new SecurityUser();
        securityUser.setPermissionValueList(admin);
        securityUser.setUser(user);
        log.info("securityUser:{}",securityUser.toString());
        return securityUser;
    }
}

配置数据库User对象映射类

package security.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = -5461108964440966122L;
    private Integer id;
    private String username;
    private String password;
    private Integer enabled;
    private Integer locked;
}

配置UserDetailService使用的SecurityUser类

package security.vo;

import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
 * UserDetailService 使用该类,该类必须是 UserDetails 的子类
 */
@Data
public class SecurityUser implements UserDetails {
    // 登录用户的基本信息
    private User user;
    //当前权限
    private List<String> permissionValueList;
    public SecurityUser() {
    }
    public SecurityUser(User user) {
        if (user != null) {
            this.user = user;
        }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        permissionValueList.forEach(permission ->{
            if(!StringUtils.isEmpty(permission)){
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission);
                authorities.add(authority);
            }
        });
        return authorities;
    public String getPassword() {
        return user.getPassword();
    public String getUsername() {
        return user.getUsername();
    public boolean isAccountNonExpired() {
        return true;
    public boolean isAccountNonLocked() {
    public boolean isCredentialsNonExpired() {
    public boolean isEnabled() {
}

配置mybatis-plus

首先,需要配置application.properties数据库连接源。

spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://106.55.137.66:3306/security?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

server.port=80

其次,需要配置Mapper类,查询数据库获取基本数据信息。

package security.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
import security.vo.User;
@Repository
public interface UserMapper extends BaseMapper<User> {
}

配置security配置类

package security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import security.config.handler.*;
import security.filter.TokenAuthFilter;
import security.filter.TokenLoginFilter;
import security.service.UserDetailService;
/**
 * security 配置类
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)  // 方法增加权限
public class MyTokenSecurityConfig extends WebSecurityConfigurerAdapter {
    // 将 UserDetailService 注入,使其去查询数据库
    @Autowired
    private UserDetailService userDetailsService;
    // token 生成器
    @Autowired
    private TokenJwtManager tokenManager;
    // 自定义密码加密解密
    @Autowired
    private DefaultPwdEndoder defaultPwdEndoder;
    // 未登录handler
    @Autowired
    private MyUnAuthEntryPoint myUnAuthEntryPoint;
    // 无权限
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;
    //  登出handler处理
    @Autowired
    private MyLogoutHandler myLogoutHandler;
    // 登录失败
    @Autowired
    private LoginFailedHandler loginFailedHandler;
    // 登录成功
    @Autowired
    private LoginSuccessHandler loginSuccessHandler;
    /**
     * 登录时,从数据库获取基本信息和权限信息
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 设置 userDetailsService 和 密码解析
        auth.userDetailsService(userDetailsService).passwordEncoder(defaultPwdEndoder);
    }
    /**
     * 配置访问过滤
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling()
                .authenticationEntryPoint(myUnAuthEntryPoint) // 未登录 handler
                .accessDeniedHandler(myAccessDeniedHandler) // 无权限
                .and().csrf().disable() // 关闭 csrf 跨域请求
                .formLogin()
                .loginProcessingUrl("/user/login")  // 设定登录请求接口
                .usernameParameter("userName")
                .passwordParameter("passWord")
                //.successHandler(loginSuccessHandler) // 因为有了 TokenLoginFilter 配置过滤器,此处配置没用
                //.failureHandler(loginFailedHandler) // 因为有了 TokenLoginFilter 配置过滤器,此处配置没用
                .permitAll()
                .and()
                .authorizeRequests() // 请求设置
                .antMatchers("/test").permitAll() // 配置不需要认证的接口
                .anyRequest().authenticated() // 任何请求都需要认证
                .and()
                .logout() // logout设定
                .logoutUrl("/logouts")  //退出请求  /logouts 未定义,交给自定义handler实现功能
                .addLogoutHandler(myLogoutHandler) // 登出 myLogoutHandler 处理
                .and()
                .addFilter(new TokenLoginFilter(tokenManager,authenticationManager())) // 认证交给 自定义 TokenLoginFilter 实现
                .addFilter(new TokenAuthFilter(authenticationManager(),tokenManager))
                .httpBasic();
    }
    /**
     * 配置不需要验证的访问路径
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        //web.ignoring().antMatchers("/test","/user/login");
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    }
}

配置几个测试接口

package security.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
    @RequestMapping("/test")
    public String test(){
        return "不需要认证就能访问";
    }
}
package security.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
    @RequestMapping("/user/test1")
    @PreAuthorize("hasAnyAuthority('admin','user')")
    public String test1(){
        return "需要认证的 /user/test1";
    }
    @RequestMapping("/user/test2")
    @PreAuthorize("hasAnyAuthority('test')")
    public String test2(){
        return "需要认证的 /user/test2";
}

Md5加密工具类

package security.utils;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
/**
 * 加密工具类
 */
public class Md5Utils {
    public static String md5(String str) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(str.getBytes());
            byte b[] = md.digest();
            str = byteToStr(b);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }
    public static String byteToStr(byte[] b){
        int i;
        StringBuffer buf = new StringBuffer("");
        for (int offset = 0; offset < b.length; offset++) {
            i = b[offset];
            //System.out.println(i);
            if (i < 0)
                i += 256;
            if (i < 16)
                buf.append("0");
            buf.append(Integer.toHexString(i));
        return buf.toString();
    /**
     * 传入文本内容,返回 SHA-256 串
     *
     * @param strText
     * @return
     */
    public static String SHA256(final String strText)
    {
        return SHA(strText, "SHA-256");
    public static String SHA1(final String strText)
        return SHA(strText, "SHA-1");
     * 传入文本内容,返回 SHA-512 串
    public static String SHA512(final String strText)
        return SHA(strText, "SHA-512");
     * 字符串 SHA 加密
    private static String SHA(final String strText, final String strType)
        // 返回值
        String strResult = null;
        // 是否是有效字符串
        if (strText != null && strText.length() > 0)
        {
            try
            {
                // SHA 加密开始
                MessageDigest messageDigest = MessageDigest.getInstance(strType);
                // 传入要加密的字符串
                messageDigest.update(strText.getBytes("utf-8"));
                // 得到 byte 类型的结果
                byte byteBuffer[] = messageDigest.digest();
                strResult = byteToStr(byteBuffer);
            }
            catch (NoSuchAlgorithmException e)
                e.printStackTrace();
            }catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
        return strResult;
    public static String base64(String str){
        String baseStr = null;
        Base64.Encoder encoder = Base64.getEncoder();
        byte[] textByte;
            textByte = str.getBytes("UTF-8");
            baseStr = encoder.encodeToString(textByte);
        } catch (UnsupportedEncodingException e) {
        return baseStr;
    public static void main(String[] args) {
        String password = "bunana1";
        System.out.println(md5(password));
        //String base64 = base64(sha512);
        //System.out.println(base64);
        //String pwd1 = md5(base64);
        //System.out.println(pwd1);
}

测试

测试采取ApiPost 工具,让测试更接近前后分离。

首先测试登录

Post
localhost/user/login

账号密码有一个不对时。

正确的账号密码

测试存在权限的接口

localhost/user/test1

测试不存在权限的接口

localhost/user/test2

测试登出

localhost/logouts

测试不需要权限的接口

localhost/test

数据库sql脚本

CREATE TABLE `user` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, -- 主键
  `username` varchar(255) DEFAULT NULL,    -- 用户名
  `password` varchar(255) DEFAULT NULL,    -- 用户密码
  `enabled` tinyint(1) DEFAULT '1',        -- 是否启用 1-启用 0-未启用
  `locked` tinyint(1) DEFAULT '0',         -- 是否被锁 1-已锁 0-未锁
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

数据为:

insert INTO user(username,password,enabled,locked) VALUES("xiangjiaoSS","1babad058e03c5296a94a5a8d7d6dd8a",1,0); -- bunana 的md5 值
insert INTO user(username,password,enabled,locked) VALUES("xiangjiaoSS2","0b13310f8db2dc22e7ddd0cdc5f0a61a",1,0); -- bunana1 的md5 值
insert INTO user(username,password,enabled,locked) VALUES("xiangjiaoSS3","b3fbcd9c9d97e47f263a19a0e01efc7d",1,0); -- bunana2 的md5 值

代码下载

springboot-security-10-qianhou

gitee 代码下载地址

到此这篇关于Spring Security前后分离校验token的文章就介绍到这了,更多相关Spring Security校验token内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 使用JWT作为Spring Security OAuth2的token存储问题

    目录 序 授权服务器整合JWT--对称加解密算法 资源服务器整合JWT--对称加解密算法 OAuth整合JWT--非对称加解密RSA 测试验证 测试通过 序 Spring Security OAuth2的demo在前几篇文章中已经讲过了,在那些模式中使用的都是RemoteTokenService调用授权服务器来校验token,返回校验通过的用户信息供上下文中获取 这种方式会加重授权服务器的负载,你想啊,当用户没授权时候获取token得找授权服务器,有token了访问资源服务器还要访问授权服务器,

  • 使用SpringSecurity 进行自定义Token校验

    背景 Spring Security默认使用「用户名/密码」的方式进行登陆校验,并通过cookie的方式存留登陆信息.在一些定制化场景,比如希望单独使用token串进行部分页面的访问权限控制时,默认方案无法支持. 在未能在网上搜索出相关实践的情况下,通过官方文档及个别Stack Overflow的零散案例,形成整体思路并实践测试通过,本文即关于该方案的一个分享. 参考官方文档 SpringSecurity校验流程 基本的SpringSecurity使用方式网上很多,不是本文关注的重点. 关于校验

  • springsecurity基于token的认证方式

    目录 前言 基于token的表单登录 基于token的短信验证码登录 基于token的社交登录 简化的OAuth的授权改造 标准的OAuth授权改造 关于用户的绑定 之前的社交登录绑定用户 自定义providerSignUtils 总结 前言 上一篇博客简析了一下spring security oauth中生成AccessToken的源码,目的就是为了方便我们将原有的表单登录,短信登录以及社交登录的认证方法,都改造成基于AccessToken的认证方式 基于token的表单登录 在简析了spri

  • Springsecurity Oauth2如何设置token的过期时间

    1.设置token的过期时间 如果我们是从数据库来读取客户端信息的话 我们只需要在数据库设置token的过期时间 1.1 oauth_client_details表每个列的作用 client_id:客户端的id 用于唯一标识每一个客户端(client):注册时必须填写(也可以服务端自动生成),这个字段是必须的,实际应用也有叫app_key resource_ids:资源服务器的id,多个用,(逗号)隔开 客户端能访问的资源id集合,注册客户端时,根据实际需要可选择资源id,也可以根据不同的额注册

  • Spring Security前后分离校验token的实现方法

    目录 前言 token配置 引入JWT依赖文件 配置token管理器类 security配置 配置未登录处理类 配置无权限处理类 配置登出操作处理类 配置token认证过滤器 配置token权限校验过滤器 自定义加密类 配置UserDetailService 配置数据库User对象映射类 配置UserDetailService使用的SecurityUser类 配置mybatis-plus 配置security配置类 配置几个测试接口 Md5加密工具类 测试 首先测试登录 测试存在权限的接口 测试

  • spring security在分布式项目下的配置方法(案例详解)

    分布式项目和传统项目的区别就是,分布式项目有多个服务,每一个服务仅仅只实现一套系统中一个或几个功能,所有的服务组合在一起才能实现系统的完整功能.这会产生一个问题,多个服务之间session不能共享,你在其中一个服务中登录了,登录信息保存在这个服务的session中,别的服务不知道啊,所以你访问别的服务还得在重新登录一次,对用户十分不友好.为了解决这个问题,于是就产生了单点登录: **jwt单点登录:**就是用户在登录服务登录成功后,登录服务会产生向前端响应一个token(令牌),以后用户再访问系

  • Spring Security验证流程剖析及自定义验证方法

    Spring Security的本质 Spring Security 本质上是一连串的 Filter , 然后又以一个独立的 Filter 的形式插入到 Filter Chain 里,其名为 FilterChainProxy . 如图所示. 实际上 FilterChainProxy 下面可以有多条 Filter Chain ,来针对不同的URL做验证,而 Filter Chain 中所拥有的 Filter 则会根据定义的服务自动增减.所以无需要显示再定义这些 Filter ,除非想要实现自己的逻

  • Spring Security 表单登录功能的实现方法

    1.简介 本文将重点介绍使用 Spring Security 登录. 本文将构建在之前简单的 Spring MVC示例 之上,因为这是设置Web应用程序和登录机制的必不可少的. 2. Maven 依赖 要将Maven依赖项添加到项目中,请参阅Spring Security with Maven 一文. 标准的 spring-security-web 和 spring-security-config 都是必需的. 3. Spring Security Java配置 我们首先创建一个扩展 WebSe

  • Spring Security基于散列加密方案实现自动登录功能

    目录 前言 一. 自动登录简介 1. 为什么要自动登录 2. 自动登录的实现方案 二. 基于散列加密方案实现自动登录 1. 配置加密令牌的key 2. 配置SecurityConfig类 3. 添加测试接口 4. 启动项目测试 三. 散列加密方案实现原理 1. cookie的加密原理分析 2. cookie的解码原理分析 3. 自动登录的源码分析 3.1 令牌生成的源码分析 3.2 令牌解析的源码分析 前言 在前面的2个章节中,一一哥 带大家实现了在Spring Security中添加图形验证码

  • Spring Security全新版本使用方式

    目录 基本使用 升级版本 旧用法 新用法 高级使用 基于方法的动态权限 基于路径的动态权限 效果测试 总结 参考资料 项目源码地址 前不久Spring Boot 2.7.0 刚刚发布,Spring Security 也升级到了5.7.1 .升级后发现,原来一直在用的Spring Security配置方法,居然已经被弃用了.不禁感慨技术更新真快,用着用着就被弃用了!今天带大家体验下Spring Security的最新用法,看看是不是够优雅! SpringBoot实战电商项目mall(50k+sta

  • Spring Security 图片验证码功能的实例代码

    验证码逻辑 以前在项目中也做过验证码,生成验证码的代码网上有很多,也有一些第三方的jar包也可以生成漂亮的验证码.验证码逻辑很简单,就是在登录页放一个image标签,src指向一个controller,这个Controller返回把生成的图片以输出流返回给页面,生成图片的同时把图片上的文本放在session,登录的时候带过来输入的验证码,从session中取出,两者对比.这位老师讲的用Spring Security集成验证码,大体思路和我说的一样,但更加规范和通用些. spring securi

  • Spring Security在标准登录表单中添加一个额外的字段

    概述 在本文中,我们将通过向标准登录表单添加额外字段来实现Spring Security的自定义身份验证方案. 我们将重点关注两种不同的方法,以展示框架的多功能性以及我们可以使用它的灵活方式. 我们的第一种方法是一个简单的解决方案,专注于重用现有的核心Spring Security实现. 我们的第二种方法是更加定制的解决方案,可能更适合高级用例. 2. Maven设置 我们将使用Spring Boot启动程序来引导我们的项目并引入所有必需的依赖项.  我们将使用的设置需要父声明,Web启动器和安

  • spring security获取用户信息的实现代码

    前言 我们在使用spring security的时候可以通过好几种方法获取用户信息, 但是今天这篇文章介绍的是一个笔者觉得最优雅的实现; 借鉴现有的spring security controller自动注入参数的方法, 我们来进一步的实现更适合我们业务的用户信息获取方法; 思路 现在spring security会在controller自动注入Authentication/Userdetails等参数, 我们拿到这些对象之后还需要一些处理才可以拿到我们需要的信息, 例如用户ID; 那获取用户I

  • Spring Security代码实现JWT接口权限授予与校验功能

    通过笔者前两篇文章的说明,相信大家已经知道JWT是什么,怎么用,该如何结合Spring Security使用.那么本节就用代码来具体的实现一下JWT登录认证及鉴权的流程. 一.环境准备工作 建立Spring Boot项目并集成了Spring Security,项目可以正常启动 通过controller写一个HTTP的GET方法服务接口,比如:"/hello" 实现最基本的动态数据验证及权限分配,即实现UserDetailsService接口和UserDetails接口.这两个接口都是向

随机推荐