Spring boot 整合Spring Security Jwt

2 篇文章 0 订阅

记录学习Spring boot 整合Spring Security Jwt

学习参考 – 慢慢的干货

https://shimo.im/docs/OnZDwoxFFL8bnP1c/read

首先创建Spring Boot项目以及导入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>

        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>1.7</version>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--security-->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

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

        <!--hutool工具-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.5.7</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

application.yml配置

# 数据库相关配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/securitytext?serverTimezone=GMT&characterEncoding=utf-8
    username: root
    password: 1233
    dbcp2:
      test-on-borrow: true
      validation-query: SELECT 1
    hikari:
      max-lifetime: 30000
  servlet:
    multipart:
      max-file-size: 1024MB
      max-request-size: 1024MB

# mybatis-plus相关配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#  mapper-locations: classpath*:/mapper/**Mapper.xml

# 服务端口号
server:
  port: 8030

# jwt配置
xiaojiang:
  jwt:
    secret: f4e2e52034348f86b67cde581c0f9eb5
    expire: 604800
    header: Authorization

创建数据库

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------

DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `username` varchar(64) DEFAULT NULL COMMENT '用户名',
  `password` varchar(64) DEFAULT NULL COMMENT '用户密码',
  `avatar` varchar(255) DEFAULT NULL COMMENT '用户头像',
  `email` varchar(64) DEFAULT NULL COMMENT '用户邮箱',
  `city` varchar(64) DEFAULT NULL COMMENT '城市',
  `created_time` datetime DEFAULT NULL COMMENT '用户创建时间',
  `updated_time` datetime DEFAULT NULL COMMENT '用户修改时间',
  `last_login_time` datetime DEFAULT NULL COMMENT '用户上次登录时间',
  `statu` int(5) NOT NULL COMMENT '用户状态 1可用|0不可用',
  PRIMARY KEY (`id`),
  UNIQUE KEY `UK_USERNAME` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '$2a$10$R7zegeWzOXPw871CmNuJ6upC0v8D373GuLuTw8jn6NET4BkPRZfgK', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', '123@qq.com', '广州', '2021-01-12 22:13:53', '2021-01-16 16:57:32', '2020-12-30 08:38:37', '1');

INSERT INTO `sys_user` VALUES ('2', 'test', '$2a$10$0ilP4ZD1kLugYwLCs4pmb.ZT9cFqzOZTNaMiHxrBnVIQUGUwEvBIO', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', 'test@qq.com', null, '2021-01-30 08:20:22', '2021-01-30 08:55:57', null, '1');


-- ----------------------------
-- Table structure for sys_role
-- ----------------------------

DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色id',
  `name` varchar(64) NOT NULL COMMENT '角色名称',
  `code` varchar(64) NOT NULL COMMENT '角色',
  `created_time` datetime DEFAULT NULL COMMENT'角色创建时间',
  `updated_time` datetime DEFAULT NULL COMMENT '角色修改时间',
  `statu` int(5) NOT NULL COMMENT '角色状态 1可用|0不可用',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`) USING BTREE,
  UNIQUE KEY `code` (`code`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', '超级管理员', 'admin', '2021-01-16 13:29:03', '2021-01-17 15:50:45', '1');

INSERT INTO `sys_role` VALUES ('2', '普通用户', 'normal', '2021-01-04 10:09:14', '2021-01-30 08:19:52', '1');


-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL COMMENT '用户id',
  `role_id` bigint(20) NOT NULL COMMENT '角色id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4;


-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1', '1');
INSERT INTO `sys_user_role` VALUES ('2', '1', '2');
INSERT INTO `sys_user_role` VALUES ('3', '2', '2');

在这里插入图片描述

补上两张菜单表

在这里插入图片描述

-- ----------------------------
-- Table structure for sys_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (
  `id` int(20) NOT NULL AUTO_INCREMENT,
  `parent_id` int(20) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
  `name` varchar(64) NOT NULL COMMENT '菜单名称',
  `path` varchar(255) DEFAULT NULL COMMENT '菜单URL',
  `perms` varchar(255) DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',
  `component` varchar(255) DEFAULT NULL,
  `type` int(5) NOT NULL COMMENT '类型     0:目录   1:菜单   2:按钮',
  `icon` varchar(32) DEFAULT NULL COMMENT '菜单图标',
  `created_time` datetime NOT NULL COMMENT '菜单创建时间',
  `updated_time` datetime DEFAULT NULL COMMENT '菜单修改时间',
  `statu` int(5) NOT NULL COMMENT '状态1可用|0不可用',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_menu
-- ----------------------------
INSERT INTO `sys_menu` VALUES ('1', '0', '系统管理', '', 'sys:manage', '', '0', 'el-icon-s-operation', '2021-01-15 18:58:18', '2021-01-15 18:58:20', '1');

INSERT INTO `sys_menu` VALUES ('2', '1', '用户管理', '/sys/users', 'sys:user:list', 'sys/User', '1', 'el-icon-s-custom', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');

INSERT INTO `sys_menu` VALUES ('3', '1', '角色管理', '/sys/roles', 'sys:role:list', 'sys/Role', '1', 'el-icon-rank', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');

INSERT INTO `sys_menu` VALUES ('4', '1', '菜单管理', '/sys/menus', 'sys:menu:list', 'sys/Menu', '1', 'el-icon-menu', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');

INSERT INTO `sys_menu` VALUES ('5', '0', '系统工具', '', 'sys:tools', null, '0', 'el-icon-s-tools', '2021-01-15 19:06:11', null, '1');

INSERT INTO `sys_menu` VALUES ('6', '5', '数字字典', '/sys/dicts', 'sys:dict:list', 'sys/Dict', '1', 'el-icon-s-order', '2021-01-15 19:07:18', '2021-01-18 16:32:13', '1');

INSERT INTO `sys_menu` VALUES ('7', '3', '添加角色', '', 'sys:role:save', '', '2', '', '2021-01-15 23:02:25', '2021-01-17 21:53:14', '0');

INSERT INTO `sys_menu` VALUES ('8', '2', '添加用户', null, 'sys:user:save', null, '2', null, '2021-01-17 21:48:32', null, '1');


-- ----------------------------
-- Table structure for sys_role_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu` (
  `id` int(20) NOT NULL AUTO_INCREMENT,
  `role_id` int(20) NOT NULL,
  `menu_id` int(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of sys_role_menu
-- ----------------------------
INSERT INTO `sys_role_menu` VALUES ('1', '1', '1');
INSERT INTO `sys_role_menu` VALUES ('2', '1', '2');
INSERT INTO `sys_role_menu` VALUES ('3', '1', '3');
INSERT INTO `sys_role_menu` VALUES ('4', '1', '4');
INSERT INTO `sys_role_menu` VALUES ('5', '1', '5');
INSERT INTO `sys_role_menu` VALUES ('6', '1', '6');
INSERT INTO `sys_role_menu` VALUES ('7', '1', '7');
INSERT INTO `sys_role_menu` VALUES ('8', '1', '8');
INSERT INTO `sys_role_menu` VALUES ('9', '2', '1');
INSERT INTO `sys_role_menu` VALUES ('10', '2', '2');
INSERT INTO `sys_role_menu` VALUES ('11', '2', '3');
INSERT INTO `sys_role_menu` VALUES ('12', '2', '4');
INSERT INTO `sys_role_menu` VALUES ('13', '2', '5');

使用mybatis-plus代码生成器

public class CodeGenerator {

    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String input= scanner.next();
            if (StringUtils.isNotEmpty(input)) {
                return input;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }


    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("Mr.jiang");
        gc.setOpen(false);
        gc.setServiceName("%sService");
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.MYSQL);
        dsc.setUrl("jdbc:mysql://localhost:3306/securitytext?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("1233");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(null);
        pc.setParent("com.jsp");
        mpg.setPackageInfo(pc);


        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix("sys_");
        mpg.setStrategy(strategy);
        mpg.execute();

    }
}

创建mybatis-plus配置类

@Configuration
public class MybatisPlusConfig {

    // 分页配置
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        return paginationInterceptor;
    }
}
@Component
public class MyMetaObjectConfig implements MetaObjectHandler {

	// 自动插入、更新
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

这里是为了entiy类中的字段

      /**
     * 用户创建时间
     */
      @TableField(fill = FieldFill.INSERT)
      @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone = "GMT+8")
      private LocalDateTime createdTime;

      /**
     * 用户修改时间
     */
      @TableField(fill = FieldFill.INSERT)
      @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone = "GMT+8")
      private LocalDateTime updatedTime;

      /**
     * 用户上次登录时间
     */
      @TableField(fill = FieldFill.INSERT_UPDATE)
      @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone = "GMT+8")
      private LocalDateTime lastLoginTime;

Result结果集

@Data
public class Result implements Serializable {

    private int code;
    private String message;
    private Object data;

    Result(int code,String message,Object data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    Result(int code,String message) {
        this.code = code;
        this.message = message;
    }
}
public enum ResultCode {

    //成功
    SUCCESS(200),

    //失败
    FAIL(400),

    //用户未认证
    UNAUTHORIZED(401),

    //url找不到
    NOT_FOUND(404),

    //访问受限
    FORBIDDEN(403),

    //服务器错误
    INTERNAL_SERVER_ERROR(500);

    public int code;

    ResultCode(int code) {
        this.code = code;
    }
}
public class ResultFactory {

    //成功
    public static Result successful(Object data) {
        return buildResult(ResultCode.SUCCESS, "成功", data);
    }

    //失败
    public static Result fail(String message) {
        return buildResult(ResultCode.FAIL, message, null);
    }

    public static Result buildResult(int ResultCode, String message, Object data) {
        return new Result(ResultCode, message, data);
    }


    public static Result buildResult(ResultCode ResultCode, String message, Object data) {
        return new Result(ResultCode.code, message, data);
    }

    public static Result buildResult(ResultCode ResultCode, String message) {
        return new Result(ResultCode.code, message);
    }

}

** 重点 Spring Security 配置类

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private LoginFailureHandler loginFailureHandler;

    @Autowired
    private LoginSuccessHandler loginSuccessHandler;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private JwtAccessDeniedHandler jwtAccessDeniedHandler;

    @Autowired
    private JwtLogoutSuccessHandler jwtLogoutSuccessHandler;

    @Bean
    JWTAuthenticationFilter jwtAuthenticationFilter() throws Exception {

        return new JWTAuthenticationFilter(authenticationManager());
    }

    @Bean
    BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //白名单
    public static final String[] URL_WHITELIST = {
            "/favicon.ico",
            "/login",
            "/logout",
    };

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                //关闭session
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

                .and()
                .formLogin()
                .successHandler(loginSuccessHandler)
                .failureHandler(loginFailureHandler)


                //退出
                .and()
                .logout()
                .logoutSuccessHandler(jwtLogoutSuccessHandler)

                //资源访问控制
                .and()
                .authorizeRequests()

                //白名单
                .antMatchers(URL_WHITELIST).permitAll()

                //其他请求需要认证
                .anyRequest().authenticated()

                //异常处理器
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .accessDeniedHandler(jwtAccessDeniedHandler)

                .and()
                .addFilter(jwtAuthenticationFilter());


    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
}

重写UserDetails

public class MyUserDetails implements UserDetails {

    private Integer id;

    private String password;

    private final String username;

    private final Collection<? extends GrantedAuthority> authorities;

    private final boolean accountNonExpired;

    private final boolean accountNonLocked;

    private final boolean credentialsNonExpired;

    private final boolean enabled;

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


    public MyUserDetails(Integer id, String username, String password, boolean enabled, boolean accountNonExpired,
                         boolean credentialsNonExpired, boolean accountNonLocked,
                         Collection<? extends GrantedAuthority> authorities) {
        Assert.isTrue(username != null && !"".equals(username) && password != null,
                "Cannot pass null or empty values to constructor");
        this.id = id;
        this.username = username;
        this.password = password;
        this.enabled = enabled;
        this.accountNonExpired = accountNonExpired;
        this.credentialsNonExpired = credentialsNonExpired;
        this.accountNonLocked = accountNonLocked;
        this.authorities = authorities;
    }


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    @Override
    public String getPassword() {
        return this.password;
    }

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

    @Override
    public boolean isAccountNonExpired() {
        return this.accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return this.accountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return this.credentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        return this.enabled;
    }
}

重写UserDetailsService

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //查询数据库判断username是否存在,如果不存在则抛出异常UsernameNotFoundException
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        User user = userMapper.selectOne(wrapper);
        if (user == null) {
            throw new UsernameNotFoundException("用户名或密码不正确!");
        }
        //从数据库查出的用户信息id,username,password,authorityList

        List<GrantedAuthority> authorityList = getUserAuthority(user.getId());
        return new MyUserDetails(user.getId(), user.getUsername(), user.getPassword(), authorityList);
    }

    public List<GrantedAuthority> getUserAuthority(Integer id) {
        return AuthorityUtils.commaSeparatedStringToAuthorityList(
                userService.getUserAuthorityInfo(id)
        );
    }
}
public interface UserRoleMapper extends BaseMapper<UserRole> {

    @Select("SELECT code, name FROM sys_user_role JOIN sys_role ON sys_user_role.role_id = sys_role.id WHERE user_id = #{uid};")
    List<Role> selectRoles(Integer uid);

}
public interface UserMapper extends BaseMapper<User> {

    @Select("SELECT rm.menu_id FROM sys_user_role ur LEFT JOIN sys_role_menu rm ON ur.role_id = rm.role_id WHERE ur.user_id = #{uid};")
    List<Integer> getNavMenuIds(Integer uid);

}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private UserMapper userMapper;

    //根据用户id获取用户角色
    @Override
    public String getUserAuthorityInfo(Integer id) {
        List<Role> roles = userRoleMapper.selectRoles(id);

        List<Integer> menuIds = userMapper.getNavMenuIds(id);
        List<Menu> menus = menuMapper.selectBatchIds(menuIds);
        String roleNames = roles.stream().map(role -> "ROLE_" + role.getCode()).collect(Collectors.joining(","));
        String permNames  = menus.stream().map(menu -> menu.getPerms()).collect(Collectors.joining(","));

//        ROLE_admin,ROLE_normal,sys:user:list...,
        String authority = roleNames.concat(",").concat(permNames);

        return authority;
    }

    @Override
    public User getByUsername(String username) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        User user = userMapper.selectOne(wrapper);
        return user;
    }
}

LoginSuccessHandler

@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    private JwtUtil jwtUtil;


    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        ServletOutputStream outputStream = response.getOutputStream();

        // 生成jwt返回
        String token = jwtUtil.generateToken(authentication.getName());
        response.setHeader(jwtUtil.getHeader(), token);

        Result result = ResultFactory.successful("");
        outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));
        outputStream.flush();
        outputStream.close();
    }
}

LoginFailureHandler

@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        ServletOutputStream outputStream = response.getOutputStream();
        Result result = ResultFactory.fail("Bad credentials".equals(exception.getMessage()) ? "用户名或密码不正确" : exception.getMessage());
        outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));

        outputStream.flush();
        outputStream.close();
    }
}

JwtLogoutSuccessHandler

@Component
public class JwtLogoutSuccessHandler implements LogoutSuccessHandler {

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        if (authentication != null) {
            new SecurityContextLogoutHandler().logout(request, response, authentication);
        }
        response.setContentType("application/json;charset=UTF-8");
        ServletOutputStream outputStream = response.getOutputStream();
        response.setHeader(jwtUtil.getHeader(), "");

        Result result = ResultFactory.successful("");

        outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));

        outputStream.flush();
        outputStream.close();
    }
}

JwtAuthenticationEntryPoint

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        ServletOutputStream outputStream = response.getOutputStream();

        Result result = ResultFactory.buildResult(ResultCode.UNAUTHORIZED, "请先登录!");

        outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));
        outputStream.flush();
        outputStream.close();
    }
}

JwtAccessDeniedHandler

@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {

        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        ServletOutputStream outputStream = response.getOutputStream();

        //Result result = ResultFactory.fail(e.getMessage());
        Result result = ResultFactory.buildResult(ResultCode.FORBIDDEN,"权限不足");


        outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));
        outputStream.flush();
        outputStream.close();
    }
}

jwt相关配置

JwtUtil

@Data
@Component
@ConfigurationProperties(prefix = "xiaojiang.jwt")
public class JwtUtil {

    private long expire;
    private String secret;
    private String header;

    //生成JWT
    public String generateToken(String username) {

        Date exireDate = new Date(new Date().getTime() + 1000 * expire);

        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(exireDate)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    //解析JWT
    public Claims getClaimByToken(String jwt) {

        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(jwt)
                    .getBody();
        } catch (Exception e) {
            return null;
        }
    }

    //JWT是否过期
    public boolean isTokenExpired(Claims claims) {
        return claims.getExpiration().before(new Date());
    }
}

JWTAuthenticationFilter

public class JWTAuthenticationFilter extends BasicAuthenticationFilter {

    @Autowired
    private UserService userService;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        String jwt = request.getHeader(jwtUtil.getHeader());
        if (StrUtil.isBlankOrUndefined(jwt)) {
            chain.doFilter(request, response);
            return;
        }
        Claims claim = jwtUtil.getClaimByToken(jwt);
        if (claim == null) {
            throw new JwtException("token异常");
        }
        if (jwtUtil.isTokenExpired(claim)) {
            throw new JwtException("token已过期");
        }
        String username = claim.getSubject();
        //获取用户的权限等信息

        User user = userService.getByUsername(username);
        List<GrantedAuthority> userAuthority = userDetailsService.getUserAuthority(user.getId());
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, userAuthority);

        System.out.println("token->" + token);

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

}

ok大功告成 使用postman试试效果

admin用户拥有admin和normal角色,test用户拥有normal角色

在Controller中分配接口权限

//  如果使用 @PreAuthorize("hasRole('ROLE_ADMIN')")需要在类的头部加上@EnableGlobalMethodSecurity(prePostEnabled = true) //打开权限验证

@RestController
@RequestMapping("/user")
@EnableGlobalMethodSecurity(prePostEnabled = true)  //打开权限验证
public class UserController {

    @Autowired
    private UserService userService;

    @PreAuthorize("hasRole('admin')")
    @GetMapping("/admin")
    public String hello() {
        return "hello admin";
    }

    @PreAuthorize("hasRole('normal')")
    @GetMapping("/test")
    public String test() {
        return "hello test";
    }

    @PreAuthorize("hasRole('admin')")
    @GetMapping("/list")
    public Result list() {
        return ResultFactory.successful(userService.list());
    }

}

首先使用admin用户登录

在这里插入图片描述

在Header中有Authorization,也就是我们需要的token

在这里插入图片描述
在这里插入图片描述

接着使用admin用户的token去访问其他接口

在这里插入图片描述

在这里插入图片描述

admin用户都可以访问

接着使用test用户登录

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用test用户去访问其他接口

在这里插入图片描述

在这里插入图片描述

因为test用户只有normal角色,所以只能访问test接口,admin接口权限不足

还待完善!!!

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值