Springboot+MybatisPlus+Spring-security-Jwt Api接口实现权限认证实战

一.前言吐槽

刚开始学习,网上找了很多例子,可能我比较小白,踩了很多坑,不知道为什么有些大佬,都不屑于测试下的,直接丢仓库了,下载很多都是跑不起来的。看得我一脸懵逼的。好啦不吐槽了,记录下自己学习的东东,开始自己的表演!

二.代码

**2.1源码地址:**https://github.com/myjsonman/spring-security-kavy.git

2.2代码结构目录:
在这里插入图片描述

2.3 在IDEA 创建一个简单的Maven 工程添加pom对应的依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>
        <!--  加密的  -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-crypto</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
	<!--  JWT  -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mybatisplus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.minidev</groupId>
            <artifactId>json-smart</artifactId>
            <version>2.3</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.noggit</groupId>
            <artifactId>noggit</artifactId>
            <version>0.6</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>

核心依赖:

security:

 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>

JWt:

  <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

2.4 简单的表设计:
设计有点粗超,见谅。

权限表:
CREATE TABLE `user_roles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `role_id` int(11) DEFAULT NULL,
  `role_str` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;

用户表:
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_img` varchar(255) DEFAULT NULL,
  `updatetime` timestamp NULL DEFAULT NULL,
  `status` char(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;

角色表:
CREATE TABLE `role` (
  `id` int(50) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `updatetime` timestamp NULL DEFAULT NULL,
  `role_desc` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

在这里写下项目的流程:

1.创建一个springboot项目
2.导入springSecurity和jwt 依赖
3.创建实体类
4.创建service层
5.创建mapper层
6.创建UserDetail去实现UserDetails
7.创建JwtUserDetailsServiceImpl去实现UserDetailsService
8.创建用户登录获取权限的拦截器
9.配置config 处理拦截器
10.jwt的工具类生成token

开始Spring boot的一个简单API

2.5 实体

@Data
public class Role {

    private Integer id;

    private String roleName;

    private Timestamp updatetime;

    private String roleDesc;

}
@Data
public class User implements Serializable {

    private Integer id;
    
    @NotBlank(message = "用户名不能为空")
    @Size(min=5, max=20,message = "用户名不能小于5位,大于20位")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min=6,max = 20,message = "密码不能小于6位,大于20位")
    private String password;

    private String userImg;

    private Date updatetime;

    private String status;
}
@Data
public class UserRoles {
    private  Integer id;
    private  Integer userId;
    private  Integer roleId;
    private String roleStr;

}

2.6 service:


public interface UserService {

    /**
     * 注册用户
     * @return
     */
    void register(User user,String str);


    /**
     * 登陆
     * @param username
     * @param password
     * @return
     */
    ResponseUserToken login(String username, String password);

}

2.7 实现类

package com.kavy.springsecuritydemo.service.serviceImp;

import com.kavy.springsecuritydemo.entity.User;
import com.kavy.springsecuritydemo.entity.UserDetail;
import com.kavy.springsecuritydemo.entity.UserRoles;
import com.kavy.springsecuritydemo.exception.CustomException;
import com.kavy.springsecuritydemo.mapper.UserMapper;
import com.kavy.springsecuritydemo.mapper.UserRolesMapper;
import com.kavy.springsecuritydemo.result.ResultCode;
import com.kavy.springsecuritydemo.result.ResultJson;
import com.kavy.springsecuritydemo.service.UserService;
import com.kavy.springsecuritydemo.utils.JwtUtils;
import com.kavy.springsecuritydemo.utils.ResponseUserToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.apache.commons.lang.StringUtils;


import java.util.Date;
@Service
public class UserServiceImp implements UserService {

    @Autowired
    UserMapper userMapper;
    @Autowired
    private UserRolesMapper userRolesMapper;
    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private AuthenticationManager authenticationManager;


    @Override
    public void register(User user,String str) {

        //查询用户
        User oldUser = userMapper.findByUsername(user.getUsername());

        if (oldUser != null) {

           throw new CustomException(ResultJson.failure(ResultCode.BAD_REQUEST, "用户已存在"));
        }
        //加密
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        user.setPassword(encoder.encode(user.getPassword()));
        user.setUpdatetime(new Date(System.currentTimeMillis()));
        user.setStatus("0");
        userMapper.insert(user);

        if (StringUtils.isNotBlank(str)){
            //权限插入
            String[] roles = str.split(",");
            for (String role : roles) {
                //如果原先有绑定权限就删除
               // userRolesMapper.deleteById(user.getId());

                UserRoles userRoles = new UserRoles();
                userRoles.setUserId(user.getId());
                userRoles.setRoleId(Integer.parseInt(role));
                userRoles.setRoleStr(str);
                userRolesMapper.insert(userRoles);
            }
        }

    }

    @Override
    public ResponseUserToken login(String username, String password) {
        //用户验证
        final Authentication authentication = authenticate(username, password);
        //存储认证信息
        SecurityContextHolder.getContext().setAuthentication(authentication);
        //生成token ,查看源代码会发现调用getPrincipal()方法会返回一个实现了`UserDetails`接口的对象
        final UserDetail userDetail = (UserDetail) authentication.getPrincipal();

        //通过工具类生成token
         final String token = "Bearer "+jwtUtils.generateAccessToken(userDetail);

        //存储token
        jwtUtils.putToken(username, token);
        // 学习 测试用,把用户的信息也返回了
        return new ResponseUserToken(token, userDetail);
    }


    private Authentication authenticate(String username, String password) {
        try {
            //该方法会去调用userDetailsService.loadUserByUsername()去验证用户名和密码,如果正确,则存储该用户名密码到“security 的 context中”
            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (DisabledException | BadCredentialsException e) {
            throw new CustomException(ResultJson.failure(ResultCode.LOGIN_ERROR, e.getMessage()));
        }
    }
}

2.8 Mapper
注:说是MybatisPlus 其实就是保存数据的时候用了,不过我已经整合好了,大家Mybatis 和Plus 两个可以无缝切换使用 。 BaseMapper<> 这个是MybatisPlus 的写法;

RoleMapper:

package com.kavy.springsecuritydemo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.kavy.springsecuritydemo.entity.Role;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;


public interface RoleMapper extends BaseMapper<Role> {

    /**
     * 创建用户角色
     * @param userId
     * @param roleId
     * @return
     */
    int insertRole(long userId, long roleId);

    /**
     * 根据角色id查找角色
     * @param roleId
     * @return
     */
    Role findRoleById(long roleId);

    /**
     * 根据用户id查找该用户角色
     * @param userId
     * @return
     */
    List<Role> findRoleByUserId(@Param("userId") Integer userId);
}

UserMapper:

@Mapper
public interface UserMapper extends BaseMapper<User> {

    User findByUsername(String username);


}

UserRolesMapper:

public interface UserRolesMapper extends BaseMapper<UserRoles> {
}

roleMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kavy.springsecuritydemo.mapper.RoleMapper">



    <insert id="insertRole">
        insert into user_roles (user_id, role_id) VALUES (#{userId}, #{roleId});
    </insert>


    <select id="findRoleById" resultType="Role">
      select id, role_name, role_desc from role where id = #{roleId}
    </select>

    <select id="findByUsername" parameterType="String" resultType="User">
    SELECT id, username, password from user where username = #{username};
    </select>
    <select id="findRoleByUserId" resultType="Role">
        select * from role where id in (SELECT role_id from user_roles where user_id = #{userId});
    </select>


</mapper>

userMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kavy.springsecuritydemo.mapper.UserMapper">


    <insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        insert into user (username, password,updatetime,status) VALUES (#{username}, #{password},#{updatetime},#{status});
    </insert>
    <select id="findByUsername" parameterType="String" resultType="User">
    SELECT id, username, password from user where username = #{username};
    </select>

    <select id="queryByUsername" parameterType="String" resultType="User">
    SELECT id, username, password from user where username = #{username};
    </select>


</mapper>

Controller:

package com.kavy.springsecuritydemo.controller;

import com.kavy.springsecuritydemo.entity.User;
import com.kavy.springsecuritydemo.result.ResultCode;
import com.kavy.springsecuritydemo.result.ResultJson;
import com.kavy.springsecuritydemo.service.UserService;
import com.kavy.springsecuritydemo.utils.ResponseUserToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/api")
public class UserController {

    @Value("${jwt.header}")
    private String tokenHeader;

    @Autowired
    UserService userService;

    @GetMapping("/hello")
    public String hello(){

        return "hi security!";
    }

    /**
     * 注册
     * @param user
     */
    @PostMapping("/register")
    public ResultJson signUp( User user ,String str) {
        if (user==null){
            ResultJson.failure(ResultCode.BAD_REQUEST);
        }
        userService.register(user,str);

         return ResultJson.success();

    }

    /**
     * 获取token
     * @param user
     * @return
     */
    @PostMapping("/login")
    public ResultJson<ResponseUserToken> login(@RequestBody User user) {

        final ResponseUserToken response = userService.login(user.getUsername(), user.getPassword());
        return ResultJson.ok(response);
    }



}

标题党

卧槽 发现贴得有点啰嗦,Security 和JWT呢。别急大佬,别喷,马上=。=上代码

网咯配图(咱也画不出来,也不敢问,都是围观大佬的):

在这里插入图片描述
1.开始整合JWT认证

创建一个JwtUserDetailsServiceImpl 去实现 UserDetailsService接口,UserDetailsService接口就一个方法 loadUserByUsername()

package com.kavy.springsecuritydemo.service.serviceImp;

import com.kavy.springsecuritydemo.entity.Role;
import com.kavy.springsecuritydemo.entity.User;
import com.kavy.springsecuritydemo.entity.UserDetail;
import com.kavy.springsecuritydemo.mapper.RoleMapper;
import com.kavy.springsecuritydemo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.security.crypto.password.PasswordEncoder;

import java.util.List;

@Configuration
public class JwtUserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
   private UserMapper userMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

            User user = userMapper.findByUsername(username);
            if (user == null) {
                throw new UsernameNotFoundException(String.format("No userDetail found with username '%s'.", username));
            }
            //查询权限封装
        List<Role> roleByUserId = roleMapper.findRoleByUserId(user.getId());

        return new UserDetail(user.getUsername(),roleByUserId,user.getPassword());

}


}

再创建UserDetail 去实现UserDetails 接口,JWT通过这个接口去获取用户密码和权限

package com.kavy.springsecuritydemo.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@Data
public class UserDetail implements UserDetails {

    private long id;
    private String username;
    private String password;
    private List<Role> roles;
    //private Collection<? extends GrantedAuthority> authorities;

    public UserDetail(
            String username,
            List<Role> roles,
            String password) {
        this.username = username;
        this.password = password;
        this.roles = roles;
    }
    //getAuthorities获取用户包含的权限,返回权限集合,权限是一个继承了GrantedAuthority的对象;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // 根据自定义逻辑来返回用户权限,如果用户权限返回空或者和拦截路径对应权限不同,验证不通过
        if (!roles.isEmpty()){

            List<GrantedAuthority> authorities = new ArrayList<>();
            for (Role role : roles) {
                authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
            }
            return authorities;
        }
        return null;
    }

    //getPassword和getUsername用于获取密码和用户名;
    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    //isAccountNonExpired方法返回boolean类型,用于判断账户是否未过期,未过期返回true反之返回false;
    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    //isAccountNonLocked方法用于判断账户是否未锁定;
    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    //isCredentialsNonExpired用于判断用户凭证是否没过期,即密码是否未过期;
    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    //isEnabled方法用于判断用户是否可用。
    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return true;
    }
}

拦截器filter

package com.kavy.springsecuritydemo.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.kavy.springsecuritydemo.service.serviceImp.JwtUserDetailsServiceImpl;
import com.kavy.springsecuritydemo.utils.JwtUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import io.jsonwebtoken.ExpiredJwtException;

@Component
public class JwtRequestFilter extends OncePerRequestFilter {
	@Autowired
	private JwtUserDetailsServiceImpl jwtUserDetailsService;
	
	@Autowired
	private JwtUtils jwtTokenUtil;
	

	
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws ServletException, IOException {
		//获取header 的Authorization
		final String requestTokenHeader = request.getHeader("Authorization");
		String username = null;
		String jwtToken = null;
		// JWT报文表头的格式是"Bearer token". 去除"Bearer ",直接获取token
		// only the Token
		if (StringUtils.isNotEmpty(requestTokenHeader) && requestTokenHeader.startsWith("Bearer ")) {
			jwtToken = requestTokenHeader.substring(7);
			try {
				//获取username
				username = jwtTokenUtil.getUsernameFromToken(jwtToken);
			} catch (IllegalArgumentException e) {
				System.out.println("Unable to get JWT Token");
			} catch (ExpiredJwtException e) {
				System.out.println("JWT Token has expired");
			}
		} else {
			logger.warn("JWT Token does not begin with Bearer String");
		}
		// Once we get the token validate it.
		if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
			//获取 userDetails
			UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
			// if token is valid configure Spring Security to manually set
			// authentication
			if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
				UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
						userDetails, null, userDetails.getAuthorities());
				usernamePasswordAuthenticationToken
				.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

				SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
			}
		}
		chain.doFilter(request, response);
	}
}

错误返回:

package com.kavy.springsecuritydemo.filter;

import java.io.IOException;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

/**
 * 用来解决匿名用户访问无权限资源时的异常
 */
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
	private static final long serialVersionUID = 1L;

	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException {
		response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
	}
}

核心配置config:

package com.kavy.springsecuritydemo.config;

import com.kavy.springsecuritydemo.filter.JwtAuthenticationEntryPoint;
import com.kavy.springsecuritydemo.filter.JwtRequestFilter;
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.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @EnableWebSecurity注解继承WebSecurityConfigurerAdapter的类,这样就构成了Spring Security的配置。
 *
 */

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private UserDetailsService userDetailsService;
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    public WebSecurityConfig(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }


        @Autowired
        private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        // We don't need CSRF for this example
        httpSecurity.csrf().disable()
                //用来解决匿名用户访问无权限资源时的异常
                .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
                //禁用session 无状态
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // dont authenticate this particular request
                .and()
                .authorizeRequests()
                //注册 register 和登录 login 不需要验证 就可以访问,其他的都需要token验证才可以访问
                .antMatchers(HttpMethod.POST, "/api/login", "/api/register", "/error/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

        // disable page caching
        httpSecurity
                .headers()
                .frameOptions().sameOrigin()  // required to set for H2 else H2 Console will be blank.
                .cacheControl();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);

    }

/*    @Override
    protected void configure(HttpSecurity http) throws Exception {
         http.formLogin() // 表单方式
      //  http.httpBasic() // HTTP Basic方式
                .loginPage("/login.html")   //.loginPage("/login.html")指定了跳转到登录页面的请求URL
                .loginProcessingUrl("/login") //.loginProcessingUrl("/login")对应登录页面form表单的action="/login"
                .and()
                .authorizeRequests() // 授权配置
                .antMatchers("/login.html").permitAll()   // 表示跳转到登录页面的请求不被拦截,否则会进入无限循环。
                .anyRequest()  // 所有请求
                .authenticated() // 都需要认证
                .and().csrf().disable();
    }*/



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


}

JWT的工具类:
用来生成token的

package com.kavy.springsecuritydemo.utils;

import com.kavy.springsecuritydemo.entity.UserDetail;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


@Component
public class JwtUtils {

    private static final String CLAIM_KEY_USER_ID = "user_id";
    private static final String CLAIM_KEY_AUTHORITIES = "scope";

    private Map<String, String> tokenMap = new ConcurrentHashMap<>(32);

    //密匙
    @Value("${jwt.secret}")
    private String secret;
    //有效期
    @Value("${jwt.expiration}")
    private Long access_token_expiration;

    //刷新密匙
    @Value("${jwt.expiration}")
    private Long refresh_token_expiration;

    private final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;


    //获取token
    public String generateAccessToken(UserDetail userDetail) {
        Map<String, Object> claims = generateClaims(userDetail);
        claims.put(CLAIM_KEY_AUTHORITIES, authoritiesToArray(userDetail.getAuthorities()).get(0));
        return generateAccessToken(userDetail.getUsername(), claims);
    }

    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }


    public void putToken(String userName, String token) {
        tokenMap.put(userName, token);
    }


    /**
     * 生成token的过期时间
     * @return
     */
    private Date generateExpirationDate(long expiration) {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    /**
     * 从token中获取claims
     *
     **/
    public Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    /**
     *  获取用户名
     * @param token
     * @return
     */
    public String getUsernameFromToken(String token) {
        String username;
        try {
            final Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }
    /**
     *  获取token 过期时间
     * @param token
     * @return
     */
    public Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            final Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
        }
        return expiration;
    }

    /**
     *     判断token 是否过期
     * @param token
     * @return
     */
    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    /**
     *  判断token是否可以刷新
     * @param token
     * @return
     */
    public Boolean canTokenBeRefreshed(String token) {
        final Date created = getCreatedDateFromToken(token);
        return !isTokenExpired(token);
    }

    /**
     * 从token中获取创建时间
     * @param token
     * @return
     */
    public Date getCreatedDateFromToken(String token) {
        Date created;
        try {
            final Claims claims = getClaimsFromToken(token);
            created = claims.getIssuedAt();
        } catch (Exception e) {
            created = null;
        }
        return created;
    }
    /**
     *   刷新token
     * @param token
     * @return
     */

    public String refreshToken(String token) {
        String refreshedToken;
        try {
            final Claims claims = getClaimsFromToken(token);
            refreshedToken = generateAccessToken(claims.getSubject(), claims);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }


    /**
     *  claims
     * @param userDetail
     * @return
     */
    private Map<String, Object> generateClaims(UserDetail userDetail) {
        Map<String, Object> claims = new HashMap<>(16);
        claims.put(CLAIM_KEY_USER_ID, userDetail.getId());
        System.out.println("userDetail.getId()--------->>>"+userDetail.getId());
        System.out.println("userDetail--->"+userDetail.toString());
        return claims;
    }

    // 传入 username 过期时间 claims 获取token
    private String generateAccessToken(String subject, Map<String, Object> claims) {
        // subject  --->userDetail.getUsername()  access_token_expiration过期时间
        return generateToken(subject, claims, access_token_expiration);
    }


    private List authoritiesToArray(Collection<? extends GrantedAuthority> authorities) {
        List<String> list = new ArrayList<>();
        for (GrantedAuthority ga : authorities) {
            list.add(ga.getAuthority());
        }
        return list;
    }



    /**
     *      生成token
     * @param subject
     * @param claims
     * @param expiration
     * @return
     */
    private String generateToken(String subject, Map<String, Object> claims, long expiration) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject) //username  设置面向用户
               // .setId(UUID.randomUUID().toString())
                .setIssuedAt(new Date())  //签发时间
                .setExpiration(generateExpirationDate(expiration)) //生成token的过期时间
                .compressWith(CompressionCodecs.DEFLATE)
                .signWith(SIGNATURE_ALGORITHM, secret)  //SignatureAlgorithm.HS512, secret 生成签名
                .compact();
    }

}

整个Security就完成了,在此我只做了注册和获取token的两个,嗯,还有一些返回的封装和错误码返回处理,没有贴出来,具体可以去看下源码,其他的可以根据自己需求写了;

测试:
没有获取到token,所以会报错
在这里插入图片描述
注册:
在这里插入图片描述

注册成功,通过注册的用户和密码获取token
在这里插入图片描述

通过获取到的token,再次访问hello接口
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值