当前位置:   article > 正文

SpringSecurity

SpringSecurity

登录实战

前言

  Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架 Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。

  一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多,因为相比与SpringSecurity,Shiro的上手更加的简单。

  一般Web应用的主要进行 认证 和 授权

  认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户。

  授权:经过认证后判断当前用户是否有权限进行某个操作。

  而认证和授权也是 SpringSecurity 作为安全框架的核心功能。

登录流程:

 

 先进行登录,登录后带着生成的token进行访问,再给予对相应的权限进行操作。

  1. 在使用Spring Security构建的Web应用程序中,登录流程涉及多个关键组件,下面详细介绍这些组件及其在登录过程中扮演的角色:
  2. 1. Filter Chain(过滤器链)
  3. SecurityContextPersistenceFilter:维护安全上下文,确保线程间的安全信息传递。
  4. UsernamePasswordAuthenticationFilter:负责处理基于表单的登录请求,收集用户名和密码,调用AuthenticationManager进行验证。
  5. ConcurrentSessionFilter:管理并发会话,避免同一账号多处登录。
  6. ExceptionTranslationFilter:捕获并处理认证或授权失败的异常。
  7. FilterSecurityInterceptor:执行访问决策,依据用户权限判断是否允许访问特定资源。
  8. 2. AuthenticationManager(认证管理器)
  9. 负责处理认证请求,它接受一个Authentication对象(包含用户凭证),并返回一个经过完全填充的(已验证的或未经验证的)Authentication对象。对于基于用户名和密码的登录,Spring Security提供了DaoAuthenticationProvider,它使用UserDetailsService来检索用户信息。
  10. 3. UserDetailsService(用户详情服务)
  11. 接口定义了一个方法loadUserByUsername(String username),用于根据用户名加载用户信息。开发者需要实现这个接口,通常从数据库中查询用户信息。返回的是UserDetails对象,它包含用户的用户名、密码(通常是加密的)、权限等安全相关信息。
  12. 4. UserDetails(用户详情)
  13. 表示用户安全信息的核心接口,包含用户名、密码、账号是否过期、凭证是否过期、账号是否锁定以及赋予用户的权限集合。一个典型的实现是org.springframework.security.core.userdetails.User。
  14. 登录流程步骤(结合Spring Security组件):
  15. 请求登录页面:用户访问登录页面,该页面由Spring Security默认或自定义的登录页面处理。
  16. 提交登录信息:用户提交用户名和密码,这些信息通过UsernamePasswordAuthenticationFilter被封装成一个UsernamePasswordAuthenticationToken,并转发给AuthenticationManager。
  17. 验证用户凭证:
  18. AuthenticationManager委托给配置的AuthenticationProvider(如DaoAuthenticationProvider)处理。
  19. DaoAuthenticationProvider调用实现UserDetailsService的服务来加载用户详情(UserDetails)。
  20. 使用PasswordEncoder比较提交的密码与数据库中存储的密码哈希值,验证密码是否正确。
  21. 认证成功:
  22. 如果验证成功,AuthenticationProvider返回一个完全填充的Authentication对象,其中包含用户的角色和权限信息。
  23. AuthenticationManager将此认证对象设置到安全上下文中,使得后续请求能够访问用户信息和权限。
  24. 通常会生成一个会话或JWT,并将其发送给客户端,用于后续请求的认证。
  25. 认证失败:
  26. 认证失败时,抛出异常,由ExceptionTranslationFilter捕获并处理,可能重定向到登录页面显示错误消息,或响应HTTP 401 Unauthorized。
  27. 访问控制:
  28. 用户携带令牌访问受保护资源时,FilterSecurityInterceptor基于用户的角色和权限进行访问决策,决定是否允许访问。
  29. 如上:Spring Security提供了一个强大且灵活的安全认证与授权框架,确保了登录过程的严谨性和安全性。

依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.xinzhi</groupId>
  6. <artifactId>SpringSecurityDemo2</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <name>SpringSecurityDemo2</name>
  9. <description>SpringSecurityDemo2</description>
  10. <properties>
  11. <java.version>11</java.version>
  12. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  13. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  14. <spring-boot.version>2.6.2</spring-boot.version>
  15. </properties>
  16. <dependencies>
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter-security</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-web</artifactId>
  24. </dependency>
  25. <!-- mysql驱动 -->
  26. <dependency>
  27. <groupId>mysql</groupId>
  28. <artifactId>mysql-connector-java</artifactId>
  29. <version>8.0.33</version>
  30. </dependency>
  31. <!-- 连接池 -->
  32. <dependency>
  33. <groupId>com.alibaba</groupId>
  34. <artifactId>druid-spring-boot-starter</artifactId>
  35. <version>1.1.16</version>
  36. </dependency>
  37. <!-- mybatis -->
  38. <dependency>
  39. <groupId>org.mybatis.spring.boot</groupId>
  40. <artifactId>mybatis-spring-boot-starter</artifactId>
  41. <version>2.0.1</version>
  42. </dependency>
  43. <dependency>
  44. <groupId>org.projectlombok</groupId>
  45. <artifactId>lombok</artifactId>
  46. <optional>true</optional>
  47. </dependency>
  48. <!--mybatis-plus的springboot支持-->
  49. <dependency>
  50. <groupId>com.baomidou</groupId>
  51. <artifactId>mybatis-plus-boot-starter</artifactId>
  52. <version>3.1.1</version>
  53. </dependency>
  54. <dependency>
  55. <groupId>org.springframework.boot</groupId>
  56. <artifactId>spring-boot-starter-test</artifactId>
  57. <scope>test</scope>
  58. </dependency>
  59. <dependency>
  60. <groupId>org.springframework.boot</groupId>
  61. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  62. </dependency>
  63. <dependency>
  64. <groupId>org.springframework.security</groupId>
  65. <artifactId>spring-security-test</artifactId>
  66. <scope>test</scope>
  67. </dependency>
  68. <dependency>
  69. <groupId>com.github.penggle</groupId>
  70. <artifactId>kaptcha</artifactId>
  71. <version>2.3.2</version>
  72. <exclusions>
  73. <exclusion>
  74. <artifactId>javax.servlet-api</artifactId>
  75. <groupId>javax.servlet</groupId>
  76. </exclusion>
  77. </exclusions>
  78. </dependency>
  79. <dependency>
  80. <groupId>io.jsonwebtoken</groupId>
  81. <artifactId>jjwt</artifactId>
  82. <version>0.9.0</version>
  83. </dependency>
  84. <dependency>
  85. <groupId>javax.xml.bind</groupId>
  86. <artifactId>jaxb-api</artifactId>
  87. <version>2.3.1</version>
  88. </dependency>
  89. <dependency>
  90. <groupId>org.aspectj</groupId>
  91. <artifactId>aspectjweaver</artifactId>
  92. <version>1.8.13</version>
  93. </dependency>
  94. </dependencies>
  95. <dependencyManagement>
  96. <dependencies>
  97. <dependency>
  98. <groupId>org.springframework.boot</groupId>
  99. <artifactId>spring-boot-dependencies</artifactId>
  100. <version>${spring-boot.version}</version>
  101. <type>pom</type>
  102. <scope>import</scope>
  103. </dependency>
  104. </dependencies>
  105. </dependencyManagement>
  106. <build>
  107. <plugins>
  108. <plugin>
  109. <groupId>org.apache.maven.plugins</groupId>
  110. <artifactId>maven-compiler-plugin</artifactId>
  111. <version>3.8.1</version>
  112. <configuration>
  113. <source>11</source>
  114. <target>11</target>
  115. <encoding>UTF-8</encoding>
  116. </configuration>
  117. </plugin>
  118. </plugins>
  119. </build>
  120. </project>

application.properties配置文件

  1. # ????
  2. spring.application.name=SpringSecurityDemo
  3. # ???
  4. server.port=8081
  5. #-----------------------------Mysql??
  6. spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
  7. spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
  8. #mybatis.config-location=classpath:mybatis-config.xml
  9. mybatis-plus.type-aliases-package=com.xinzhi.model
  10. mybatis-plus.mapper-locations=classpath:mapper/*.xml
  11. mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
  12. mybatis-plus.configuration.map-underscore-to-camel-case=true
  13. #-----------------------------Mysql
  14. spring.datasource.url=jdbc:mysql://localhost:3306/jdb?useUnicode=true&characterEncoding=utf-8&useSSL=false
  15. spring.datasource.username=root
  16. spring.datasource.password=123456
  17. server.servlet.session.timeout=1m

建表

  1. SET NAMES utf8mb4;
  2. SET FOREIGN_KEY_CHECKS = 0;
  3. -- ----------------------------
  4. -- Table structure for menu
  5. -- ----------------------------
  6. DROP TABLE IF EXISTS `menu`;
  7. CREATE TABLE `menu` (
  8. `id` int(11) NOT NULL AUTO_INCREMENT,
  9. `pattern` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  10. PRIMARY KEY (`id`) USING BTREE
  11. ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
  12. -- ----------------------------
  13. -- Records of menu
  14. -- ----------------------------
  15. INSERT INTO `menu` VALUES (1, '/admin/**');
  16. INSERT INTO `menu` VALUES (2, '/user/**');
  17. INSERT INTO `menu` VALUES (3, '/guest/**');
  18. -- ----------------------------
  19. -- Table structure for menu_role
  20. -- ----------------------------
  21. DROP TABLE IF EXISTS `menu_role`;
  22. CREATE TABLE `menu_role` (
  23. `id` int(11) NOT NULL AUTO_INCREMENT,
  24. `rid` int(11) NULL DEFAULT NULL,
  25. `mid` int(11) NULL DEFAULT NULL,
  26. PRIMARY KEY (`id`) USING BTREE
  27. ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
  28. -- ----------------------------
  29. -- Records of menu_role
  30. -- ----------------------------
  31. INSERT INTO `menu_role` VALUES (1, 1, 1);
  32. INSERT INTO `menu_role` VALUES (2, 1, 2);
  33. INSERT INTO `menu_role` VALUES (3, 2, 2);
  34. INSERT INTO `menu_role` VALUES (4, 3, 3);
  35. -- ----------------------------
  36. -- Table structure for role
  37. -- ----------------------------
  38. DROP TABLE IF EXISTS `role`;
  39. CREATE TABLE `role` (
  40. `id` int(11) NOT NULL AUTO_INCREMENT,
  41. `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  42. `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  43. PRIMARY KEY (`id`) USING BTREE
  44. ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
  45. -- ----------------------------
  46. -- Records of role
  47. -- ----------------------------
  48. INSERT INTO `role` VALUES (1, 'product', '商品管理员');
  49. INSERT INTO `role` VALUES (2, 'admin', '系统管理员');
  50. INSERT INTO `role` VALUES (3, 'user', '用户管理员');
  51. -- ----------------------------
  52. -- Table structure for user
  53. -- ----------------------------
  54. DROP TABLE IF EXISTS `user`;
  55. CREATE TABLE `user` (
  56. `id` int(11) NOT NULL AUTO_INCREMENT,
  57. `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  58. `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  59. `enabled` tinyint(1) NULL DEFAULT NULL,
  60. `accountNonExpired` tinyint(1) NULL DEFAULT NULL,
  61. `accountNonLocked` tinyint(1) NULL DEFAULT NULL,
  62. `credentialsNonExpired` tinyint(1) NULL DEFAULT NULL,
  63. PRIMARY KEY (`id`) USING BTREE
  64. ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
  65. -- ----------------------------
  66. -- Records of user
  67. -- ----------------------------
  68. INSERT INTO `user` VALUES (1, 'root', '$2a$10$YeilFczw8sQ1RBthIFLRW.pI/KkdbDofOmZ0w5Wfq9mMMyL3ylC/.', 1, 1, 1, 1);
  69. INSERT INTO `user` VALUES (2, 'admin', '$2a$10$YeilFczw8sQ1RBthIFLRW.pI/KkdbDofOmZ0w5Wfq9mMMyL3ylC/.', 1, 1, 1, 1);
  70. INSERT INTO `user` VALUES (3, 'han', '$2a$10$YeilFczw8sQ1RBthIFLRW.pI/KkdbDofOmZ0w5Wfq9mMMyL3ylC/.', 1, 1, 1, 1);
  71. -- ----------------------------
  72. -- Table structure for user_role
  73. -- ----------------------------
  74. DROP TABLE IF EXISTS `user_role`;
  75. CREATE TABLE `user_role` (
  76. `id` int(11) NOT NULL AUTO_INCREMENT,
  77. `uid` int(11) NULL DEFAULT NULL,
  78. `rid` int(11) NULL DEFAULT NULL,
  79. PRIMARY KEY (`id`) USING BTREE
  80. ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
  81. -- ----------------------------
  82. -- Records of user_role
  83. -- ----------------------------
  84. INSERT INTO `user_role` VALUES (1, 1, 1);
  85. INSERT INTO `user_role` VALUES (2, 1, 2);
  86. INSERT INTO `user_role` VALUES (3, 2, 2);
  87. INSERT INTO `user_role` VALUES (4, 3, 3);
  88. SET FOREIGN_KEY_CHECKS = 1;

实体类

权限表

package com.xinzhi.model;

import java.util.List;

/**
* 权限表
*/
public class Menu {

private Integer id;

private String pattern;

private List<Role> roles;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getPattern() {
return pattern;
}

public void setPattern(String pattern) {
this.pattern = pattern;
}

public List<Role> getRoles() {
return roles;
}

public void setRoles(List<Role> roles) {
this.roles = roles;
}
}

result

package com.xinzhi.model;



import java.io.Serializable;


public class Result<T> implements Serializable {

private static final long serialVersionUID = 1L;

/**
* 成功标志
*/
private boolean success = true;

/**
* 返回处理消息
*/
private String message = "操作成功!";

/**
* 返回代码
*/
private Integer code = 0;

/**
* 返回数据对象 data
*/
private T result;

public static long getSerialVersionUID() {
return serialVersionUID;
}

public boolean isSuccess() {
return success;
}

public void setSuccess(boolean success) {
this.success = success;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}

public T getResult() {
return result;
}

public void setResult(T result) {
this.result = result;
}

public long getTimestamp() {
return timestamp;
}

public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}

/**
* 时间戳
*/
private long timestamp = System.currentTimeMillis();

public Result() {

}

public Result<T> success(String message) {
this.message = message;
this.code = 200;
this.success = true;
return this;
}


public static Result<Object> ok() {
Result<Object> r = new Result<Object>();
r.setSuccess(true);
r.setCode(200);
r.setMessage("成功");
return r;
}

public static Result<Object> ok(String msg) {
Result<Object> r = new Result<Object>();
r.setSuccess(true);
r.setCode(200);
r.setMessage(msg);
return r;
}

public static Result<Object> ok(Object data) {
Result<Object> r = new Result<Object>();
r.setSuccess(true);
r.setCode(200);
r.setResult(data);
return r;
}

public static Result<Object> error(String msg) {
return error(500, msg);
}

public static Result<Object> error(int code, String msg) {
Result<Object> r = new Result<Object>();
r.setCode(code);
r.setMessage(msg);
r.setSuccess(false);
return r;
}

public Result<T> error500(String message) {
this.message = message;
this.code = 500;
this.success = false;
return this;
}
/**
* 无权限访问返回结果
*/
public static Result<Object> noauth(String msg) {
return error(555, msg);
}
}

角色信息表

package com.xinzhi.model;

/**
* 角色信息
*/
public class Role {
/**
* id
*/
private Integer id;
/**
* 角色名
*/
private String name;
/**
* 角色说明
*/
private String content;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name == null ? null : name.trim();
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content == null ? null : content.trim();
}
}

用户信息表

package com.xinzhi.model;

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

import java.util.Collection;

/**
* 用户信息
*/
public class User implements UserDetails {

private Integer id;
private String username; //用户名
private String password; //密码
private boolean accountNonExpired; //是否没过期
private boolean accountNonLocked; //是否没被锁定
private boolean credentialsNonExpired; //密码是否没过期
private boolean enabled; //账号是否可用
private Collection<? extends GrantedAuthority> authorities; //用户的权限集合


public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public void setPassword(String password) {
this.password = password;
}

public void setUsername(String username) {
this.username = username;
}

public void setAccountNonExpired(boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}

public void setAccountNonLocked(boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}

public void setCredentialsNonExpired(boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = authorities;
}

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

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

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

@Override
public boolean isAccountNonExpired() {
return true; //暂时未用到,直接返回true,表示账户未过期
}

@Override
public boolean isAccountNonLocked() {
return true; //暂时未用到,直接返回true,表示账户未被锁定
}

@Override
public boolean isCredentialsNonExpired() {
return true; //暂时未用到,直接返回true,表示账户密码未过期
}

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

dao接口

  1. package com.xinzhi.dao;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.xinzhi.model.Menu;
  4. import com.xinzhi.model.Role;
  5. import com.xinzhi.model.User;
  6. import org.apache.ibatis.annotations.Mapper;
  7. import org.apache.ibatis.annotations.Param;
  8. import java.util.List;
  9. @Mapper
  10. public interface UserMapper extends BaseMapper<User> {
  11. User selectByUsername(String username);
  12. List<Role> selectRolesByUserId(Integer id);
  13. Integer updatePassword(@Param("username") String username, @Param("password") String password);
  14. List<Menu> selectMenuByRoleIds(@Param("roleIds")List<Integer> roleIds);
  15. }

usermapper

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
  3. <mapper namespace="com.xinzhi.dao.UserMapper" >
  4. <sql id="Base_Column_List" >
  5. id, username, password, enabled, accountNonExpired, accountNonLocked, credentialsNonExpired
  6. </sql>
  7. <update id="updatePassword">
  8. update user set password=#{password} where username=#{username}
  9. </update>
  10. <select id="selectByUsername" resultType="com.xinzhi.model.User">
  11. select
  12. <include refid="Base_Column_List" />
  13. from user
  14. where username = #{username}
  15. </select>
  16. <select id="selectRolesByUserId" resultType="com.xinzhi.model.Role">
  17. select
  18. r.name,r.id,r.content
  19. from
  20. role r
  21. left join
  22. user_role ur
  23. on r.id = ur.rid
  24. where ur.uid=#{uid}
  25. </select>
  26. <select id="selectMenuByRoleIds" resultType="com.xinzhi.model.Menu">
  27. select
  28. m.id,m.pattern
  29. from menu m
  30. left join menu_role rm on m.id=rm.mid
  31. where
  32. <foreach collection="roleIds" open="rm.rid in(" item="id" close=")" separator=",">
  33. #{id}
  34. </foreach>
  35. </select>
  36. </mapper>

Service层

  1. package com.xinzhi.service;
  2. import com.xinzhi.dao.UserMapper;
  3. import com.xinzhi.model.Menu;
  4. import com.xinzhi.model.Role;
  5. import com.xinzhi.model.User;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.security.core.authority.AuthorityUtils;
  9. import org.springframework.security.core.userdetails.UserDetails;
  10. import org.springframework.security.core.userdetails.UserDetailsService;
  11. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  12. import org.springframework.stereotype.Service;
  13. import java.util.Collections;
  14. import java.util.List;
  15. import java.util.stream.Collectors;
  16. /**
  17. * 权限
  18. */
  19. @Service
  20. @Slf4j
  21. public class SpringUserDetailsService implements UserDetailsService {
  22. @Autowired
  23. private UserMapper userMapper;
  24. @Override
  25. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  26. User user = userMapper.selectByUsername(username);
  27. // 判断用户是否存在
  28. if (user == null){
  29. throw new UsernameNotFoundException("用户不存在");
  30. }
  31. // 根据用户ID查找用户的角色列表
  32. List<Role> roles = userMapper.selectRolesByUserId(user.getId());
  33. // 获取角色ID列表
  34. List<Integer> roleIds = roles.stream().map(s -> s.getId()).collect(Collectors.toList());
  35. // 根据角色ID列表查找权限
  36. List<Menu> menus = userMapper.selectMenuByRoleIds(roleIds);
  37. // 获取角色名称列表并加上前缀 "ROLE_"
  38. List<String> collect = roles.stream().map(s -> "ROLE_" + s.getName()).collect(Collectors.toList());
  39. // 获取菜单权限模式
  40. List<String> collect1 = menus.stream().map(m -> m.getPattern()).collect(Collectors.toList());
  41. // 合并菜单权限和角色权限
  42. Collections.addAll(collect1,collect.stream().toArray(String[] ::new));
  43. // 将权限列表设置到用户对象中
  44. user.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",",collect1)));
  45. // 返回包含用户详情的User对象
  46. return user;
  47. }
  48. }

config配置类

  1. package com.xinzhi.config;
  2. import com.fasterxml.jackson.databind.ObjectMapper;
  3. import com.xinzhi.filter.CaptchaCodeFilter;
  4. import com.xinzhi.service.SpringUserDetailsService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.security.authentication.AuthenticationManager;
  9. import org.springframework.security.authorization.AuthorizationDecision;
  10. import org.springframework.security.config.BeanIds;
  11. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  12. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  13. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  14. import org.springframework.security.config.http.SessionCreationPolicy;
  15. import org.springframework.security.core.GrantedAuthority;
  16. import org.springframework.security.core.authority.SimpleGrantedAuthority;
  17. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  18. import org.springframework.security.crypto.password.PasswordEncoder;
  19. import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
  20. import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
  21. import org.springframework.web.cors.CorsConfiguration;
  22. import org.springframework.web.cors.CorsConfigurationSource;
  23. import javax.annotation.Resource;
  24. import java.util.Arrays;
  25. import java.util.Collection;
  26. import java.util.HashMap;
  27. @Configuration
  28. public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
  29. @Autowired
  30. private CaptchaCodeFilter captchaCodeFilter;
  31. @Override
  32. protected void configure(HttpSecurity http) throws Exception {
  33. // 表单认证
  34. http.formLogin()
  35. .loginProcessingUrl("/login")
  36. // .successForwardUrl("/index")
  37. // .failureForwardUrl("/fail") //登录失败跳转地址
  38. //认证成功处理
  39. .successHandler(((request, response, authentication) -> {
  40. HashMap<String, Object> map = new HashMap<>();
  41. map.put("msg", "认证成功");
  42. map.put("status", 200);
  43. map.put("用户信息", authentication.getPrincipal());
  44. response.setContentType("application/json;charset=UTF-8");
  45. String s = new ObjectMapper().writeValueAsString(map);
  46. response.getWriter().println(s);
  47. }))
  48. // 认证失败处理
  49. .failureHandler(((request, response, exception) -> {
  50. HashMap<String, Object> map = new HashMap<>();
  51. map.put("msg", "认证失败");
  52. map.put("status", 500);
  53. map.put("异常信息", exception.getMessage());
  54. response.setContentType("application/json;charset=UTF-8");
  55. String s = new ObjectMapper().writeValueAsString(map);
  56. response.getWriter().println(s);
  57. }))
  58. .loginPage("/login.html");
  59. // 拦截
  60. http.authorizeHttpRequests()
  61. .antMatchers("/login.html", "/kaptcha", "/authentication", "/refreshtoken").permitAll()
  62. .antMatchers("/fail.html").permitAll() //fail.html 不需要被认证
  63. .antMatchers("/invalidSession.html").permitAll()
  64. .anyRequest().access((authenticationSupplier, requestAuthorizationContext) -> {
  65. Collection<? extends GrantedAuthority> authorities = authenticationSupplier.get().getAuthorities();
  66. String requestURI = requestAuthorizationContext.getRequest().getRequestURI();
  67. SimpleGrantedAuthority simpleGrantedAuthority
  68. = new SimpleGrantedAuthority(requestURI);
  69. boolean contains = authorities.contains(simpleGrantedAuthority);
  70. return new AuthorizationDecision(contains);
  71. });
  72. // .antMatchers("/user/detail").hasAuthority("/user/**")
  73. // .antMatchers("/index.html").hasRole("super")
  74. // .anyRequest().authenticated();
  75. // 异常处理
  76. http.exceptionHandling()
  77. .accessDeniedHandler(((request, response, accessDeniedException) -> {
  78. HashMap<String, Object> map = new HashMap<>();
  79. map.put("msg", "禁止访问");
  80. map.put("status", 403);
  81. map.put("异常信息", accessDeniedException.getMessage());
  82. response.setContentType("application/json;charset=UTF-8");
  83. String s = new ObjectMapper().writeValueAsString(map);
  84. response.getWriter().println(s);
  85. }));
  86. // session管理
  87. http.sessionManagement()
  88. .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
  89. //.invalidSessionUrl("/invalidSession.html") //非法超时session跳转页面
  90. .maximumSessions(1) // 允许登录的最多用户数量
  91. .maxSessionsPreventsLogin(false) // true表示已经登录就不予许再次登录, false表示允许再次登录但是之前的登录账户会被踢下线
  92. .expiredSessionStrategy(new CustomExpiredSessionStrategy());
  93. /**
  94. * 退出登录
  95. */
  96. http.logout().logoutSuccessHandler(((request, response, authentication) -> {
  97. HashMap<String, Object> map = new HashMap<>();
  98. map.put("msg", "退出成功");
  99. map.put("status", 200);
  100. response.setContentType("application/json;charset=UTF-8");
  101. String s = new ObjectMapper().writeValueAsString(map);
  102. response.getWriter().println(s);
  103. }));
  104. // 验证码
  105. http.addFilterBefore(captchaCodeFilter, UsernamePasswordAuthenticationFilter.class);
  106. //
  107. /**
  108. * 记住我
  109. */
  110. http.rememberMe().tokenValiditySeconds(7 * 24 * 3600);
  111. //跨域
  112. http.csrf().disable();
  113. // 跨域
  114. // http.cors(c -> {
  115. // CorsConfigurationSource source = request -> {
  116. // CorsConfiguration config = new CorsConfiguration();
  117. // config.setAllowedOrigins(Arrays.asList("*"));
  118. // config.setAllowedMethods(Arrays.asList("*"));
  119. // config.addAllowedHeader("*");
  120. // return config;
  121. // };
  122. // c.configurationSource(source);
  123. // });
  124. // CSRF 跨站攻击
  125. // http.csrf()
  126. // .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
  127. // .ignoringAntMatchers("/authentication");
  128. }
  129. /**
  130. * 注入加密类
  131. * @return
  132. */
  133. @Bean("passwordEncoder")
  134. public PasswordEncoder passwordEncoder(){
  135. return new BCryptPasswordEncoder();
  136. }
  137. @Resource
  138. private SpringUserDetailsService userDetailsService;
  139. @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
  140. @Override
  141. protected AuthenticationManager authenticationManager() throws Exception {
  142. return super.authenticationManager();
  143. }
  144. @Override
  145. protected void configure(AuthenticationManagerBuilder builder) throws Exception {
  146. // 加密方式认证
  147. builder.userDetailsService(userDetailsService)
  148. .passwordEncoder(passwordEncoder());
  149. }
  150. }

Controller层

  1. package com.xinzhi.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.PostMapping;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.ResponseBody;
  7. @Controller
  8. @RequestMapping("/user")
  9. public class TestController {
  10. @RequestMapping("/hello")
  11. @ResponseBody
  12. public String test(){
  13. return "security";
  14. }
  15. @RequestMapping("/index")
  16. public String index(){
  17. return "redirect:/index.html";
  18. }
  19. @PostMapping("/fail")
  20. public String fail(){
  21. return "redirect:/fail.html";
  22. }
  23. @GetMapping("/detail")
  24. public String detail(){
  25. return "detail";
  26. }
  27. @GetMapping("/add")
  28. @ResponseBody
  29. public String add(){
  30. return "add";
  31. }
  32. @GetMapping("/delete")
  33. @ResponseBody
  34. public String delete(){
  35. return "delete";
  36. }
  37. @GetMapping("/update")
  38. @ResponseBody
  39. public String update(){
  40. return "update";
  41. }
  42. @GetMapping("/select")
  43. @ResponseBody
  44. public String select(){
  45. return "select";
  46. }
  47. }

页面

static

login.heml

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>登录</title></head>
  6. <body>
  7. <!--不想要jwt是把authentication换成login就是普通的了-->
  8. <form action="/authentication" method="post">
  9. 用户名:<input type="text" name="username"/> <br>
  10. 密码: <input type="password" name="password"/> <br>
  11. 验证码:<input type="text" name="captchaCode"/><img src="/kaptcha" id="kaptcha" width="110px" height="40px"/> <br>
  12. <input type="checkbox" name="remember-me" value="on"/>记住密码 <br>
  13. <input type="submit" value="提交"/>
  14. </form>
  15. <script>
  16. window.onload=function(){
  17. var kaptchaImg = document.getElementById("kaptcha");
  18. kaptchaImg.onclick = function(){
  19. kaptchaImg.src = "/kaptcha?" + Math.floor(Math.random() * 100)
  20. }
  21. }
  22. </script>
  23. </body>
  24. </html>

templates

detail.html 退出页面

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <a href="/logout" >退出</a>
  9. 详情页
  10. </body>
  11. </html>

fail.html 超时页面

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>error</title>
  6. </head>
  7. <body>
  8. 操作失败,请重新登录. <a href="/login.html">跳转</a>
  9. </body>
  10. </html>

index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. 首页
  9. </body>
  10. </html>

测试(页面上)

http://localhost:8080/login.html

记住我

在配置类里有相对应的配置类就可已了直接用就好了

在login页面里也有先对应的代码直接用就好了

退出登录

退出页面有相对应的代码

配置文件也有对应的代码

验证码

pom有对应的依赖了他的依赖是

  1. <dependency>
  2. <groupId>com.github.penggle</groupId>
  3. <artifactId>kaptcha</artifactId>
  4. <version>2.3.2</version>
  5. <exclusions>
  6. <exclusion>
  7. <artifactId>javax.servlet-api</artifactId>
  8. <groupId>javax.servlet</groupId>
  9. </exclusion>
  10. </exclusions>
  11. </dependency>

实体类

创建一个vo

CaptchaImageVO

  1. package com.xinzhi.model.vo;
  2. import lombok.Data;
  3. import java.time.LocalDateTime;
  4. /**
  5. * 验证码的实体类
  6. */
  7. @Data
  8. public class CaptchaImageVO {
  9. //验证码文字
  10. private String code;
  11. //验证码失效时间
  12. private LocalDateTime expireTime;
  13. public CaptchaImageVO(String code, int expireAfterSeconds){
  14. this.code = code;
  15. this.expireTime = LocalDateTime.now().plusSeconds(expireAfterSeconds);
  16. }
  17. //验证码是否失效
  18. public boolean isExpried() {
  19. return LocalDateTime.now().isAfter(expireTime);
  20. }
  21. public String getCode() {
  22. return code;
  23. }
  24. }

kaptcha.properties配置文件

  1. #????????
  2. kaptcha.border=no
  3. kaptcha.border.color=105,179,90
  4. kaptcha.image.width=100
  5. kaptcha.image.height=45
  6. kaptcha.session.key=code
  7. kaptcha.textproducer.font.color=blue
  8. kaptcha.textproducer.font.size=35
  9. kaptcha.textproducer.char.length=4
  10. kaptcha.textproducer.font.names=??,??,????

配置类CaptchaConfig

  1. package com.xinzhi.config;
  2. import com.google.code.kaptcha.impl.DefaultKaptcha;
  3. import com.google.code.kaptcha.util.Config;
  4. import org.springframework.beans.factory.annotation.Value;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.PropertySource;
  7. import org.springframework.stereotype.Component;
  8. import java.util.Properties;
  9. //验证码的配置文件
  10. @Component
  11. @PropertySource(value = {"classpath:kaptcha.properties"})
  12. public class CaptchaConfig {
  13. @Value("${kaptcha.border}")
  14. private String border;
  15. @Value("${kaptcha.border.color}")
  16. private String borderColor;
  17. @Value("${kaptcha.textproducer.font.color}")
  18. private String fontColor;
  19. @Value("${kaptcha.image.width}")
  20. private String imageWidth;
  21. @Value("${kaptcha.image.height}")
  22. private String imageHeight;
  23. @Value("${kaptcha.textproducer.char.length}")
  24. private String charLength;
  25. @Value("${kaptcha.textproducer.font.names}")
  26. private String fontNames;
  27. @Value("${kaptcha.textproducer.font.size}")
  28. private String fontSize;
  29. @Value("${kaptcha.session.key}")
  30. private String sessionKey;
  31. @Bean(name = "captchaProducer")
  32. public DefaultKaptcha getKaptchaBean() {
  33. DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
  34. Properties properties = new Properties();
  35. properties.setProperty("kaptcha.border", border);
  36. properties.setProperty("kaptcha.border.color", borderColor);
  37. properties.setProperty("kaptcha.textproducer.font.color", fontColor);
  38. properties.setProperty("kaptcha.image.width", imageWidth);
  39. properties.setProperty("kaptcha.image.height", imageHeight);
  40. properties.setProperty("kaptcha.session.key", sessionKey);
  41. properties.setProperty("kaptcha.textproducer.char.length", charLength);
  42. properties.setProperty("kaptcha.textproducer.font.names", fontNames);
  43. properties.setProperty("kaptcha.textproducer.font.size",fontSize);
  44. Config config = new Config(properties);
  45. defaultKaptcha.setConfig(config);
  46. return defaultKaptcha;
  47. }
  48. }

Controller层里

CaptchaController

  1. package com.xinzhi.controller;
  2. import com.google.code.kaptcha.impl.DefaultKaptcha;
  3. import com.xinzhi.model.vo.CaptchaImageVO;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RequestMethod;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import javax.annotation.Resource;
  8. import javax.imageio.ImageIO;
  9. import javax.servlet.ServletOutputStream;
  10. import javax.servlet.http.HttpServletResponse;
  11. import javax.servlet.http.HttpSession;
  12. import java.awt.image.BufferedImage;
  13. /**
  14. * 获取验证码
  15. */
  16. @RestController
  17. public class CaptchaController extends Throwable {
  18. @Resource
  19. DefaultKaptcha captchaProducer;
  20. /**
  21. * 获取验证码
  22. */
  23. @RequestMapping(value = "/kaptcha", method = RequestMethod.GET)
  24. public void kaptcha(HttpSession session, HttpServletResponse response) throws Exception {
  25. response.setDateHeader("Expires", 0);
  26. response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
  27. response.addHeader("Cache-Control", "post-check=0, pre-check=0");
  28. response.setHeader("Pragma", "no-cache");
  29. response.setContentType("image/jpeg");
  30. String capText = captchaProducer.createText();
  31. System.out.println(capText);
  32. CaptchaImageVO captchaImageVO = new CaptchaImageVO(capText,2 * 60);
  33. //将验证码存到session
  34. session.setAttribute("code", capText);
  35. //将图片返回给前端
  36. try(ServletOutputStream out = response.getOutputStream();) {
  37. BufferedImage bi = captchaProducer.createImage(capText);
  38. ImageIO.write(bi, "jpg", out);
  39. out.flush();
  40. }//使用try-with-resources不用手动关闭流
  41. }
  42. }

创建一个Filter包

  1. package com.xinzhi.filter;
  2. import com.alibaba.druid.util.StringUtils;
  3. import com.fasterxml.jackson.databind.ObjectMapper;
  4. import org.springframework.stereotype.Component;
  5. import org.springframework.web.bind.ServletRequestBindingException;
  6. import org.springframework.web.bind.ServletRequestUtils;
  7. import org.springframework.web.context.request.ServletWebRequest;
  8. import org.springframework.web.filter.OncePerRequestFilter;
  9. import javax.servlet.FilterChain;
  10. import javax.servlet.ServletException;
  11. import javax.servlet.http.HttpServletRequest;
  12. import javax.servlet.http.HttpServletResponse;
  13. import javax.servlet.http.HttpSession;
  14. import java.io.IOException;
  15. import java.util.HashMap;
  16. /**
  17. * 验证码
  18. */
  19. @Component
  20. public class CaptchaCodeFilter extends OncePerRequestFilter {
  21. @Override
  22. protected void doFilterInternal(HttpServletRequest request,
  23. HttpServletResponse response,
  24. FilterChain filterChain)
  25. throws ServletException, IOException {
  26. // 必须是登录的post请求才能进行验证,其他的直接放行
  27. if(StringUtils.equals("/login",request.getRequestURI())
  28. && StringUtils.equalsIgnoreCase(request.getMethod(),"post")){
  29. //1.验证谜底与用户输入是否匹配
  30. if (!validate(new ServletWebRequest(request), response)){
  31. return;
  32. }
  33. }
  34. //通过校验,就放行
  35. filterChain.doFilter(request,response);
  36. }
  37. private Boolean validate(ServletWebRequest request,HttpServletResponse response) throws ServletRequestBindingException, IOException {
  38. HttpSession session = request.getRequest().getSession();
  39. //获取用户登录界面输入的captchaCode
  40. String codeInRequest = ServletRequestUtils.getStringParameter(
  41. request.getRequest(),"captchaCode");
  42. if(StringUtils.isEmpty(codeInRequest)){
  43. HashMap<String, Object> map = new HashMap<>();
  44. map.put("msg", "验证码不能为空");
  45. map.put("status", 500);
  46. response.setContentType("application/json;charset=UTF-8");
  47. String s = new ObjectMapper().writeValueAsString(map);
  48. response.getWriter().println(s);
  49. return false;
  50. }
  51. // 获取session池中的验证码谜底
  52. String codeInSession = (String)
  53. session.getAttribute("code");
  54. // 请求验证码校验
  55. if(!StringUtils.equals(codeInSession, codeInRequest)) {
  56. HashMap<String, Object> map = new HashMap<>();
  57. map.put("msg", "验证码不匹配");
  58. map.put("status", 500);
  59. response.setContentType("application/json;charset=UTF-8");
  60. String s = new ObjectMapper().writeValueAsString(map);
  61. response.getWriter().println(s);
  62. return false;
  63. }
  64. return true;
  65. }
  66. }

过滤器替换 前端代码对应的里面都有不用在添加了

9 配置类中验证码路径放行

.antMatchers("/login.html", "/fail.html", "/invalidSession.html","/kaptcha").permitAll()

也有这段代码

整合JTW

  • 引入依赖 不用管也有配置类里

  1. <dependency>
  2. <groupId>io.jsonwebtoken</groupId>
  3. <artifactId>jjwt</artifactId>
  4. <version>0.9.0</version>
  5. </dependency>

 工具类

  1. package com.xinzhi.utils;
  2. import io.jsonwebtoken.Claims;
  3. import io.jsonwebtoken.Jwts;
  4. import io.jsonwebtoken.SignatureAlgorithm;
  5. import lombok.Data;
  6. import org.springframework.security.core.userdetails.UserDetails;
  7. import org.springframework.stereotype.Component;
  8. import java.util.Date;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. @Data
  12. @Component
  13. public class JwtTokenUtil {
  14. private String secret="xinzhi";
  15. private Long expiration=3600000L;
  16. private String header="JWTHeaderName";
  17. /**
  18. * 生成token令牌
  19. *
  20. * @param userDetails 用户
  21. * @return 令token牌
  22. */
  23. public String generateToken(UserDetails userDetails) {
  24. Map<String, Object> claims = new HashMap<>(2);
  25. //获取用户名
  26. claims.put("sub", userDetails.getUsername());
  27. // 创建时间
  28. claims.put("created", new Date());
  29. return generateToken(claims);
  30. }
  31. /**
  32. * 从令牌中获取用户名
  33. *
  34. * @param token 令牌
  35. * @return 用户名
  36. */
  37. public String getUsernameFromToken(String token) {
  38. String username;
  39. try {
  40. Claims claims = getClaimsFromToken(token);
  41. username = claims.getSubject();
  42. } catch (Exception e) {
  43. username = null;
  44. }
  45. return username;
  46. }
  47. /**
  48. * 判断令牌是否过期
  49. *
  50. * @param token 令牌
  51. * @return 是否过期
  52. */
  53. public Boolean isTokenExpired(String token) {
  54. try {
  55. Claims claims = getClaimsFromToken(token);
  56. Date expiration = claims.getExpiration();
  57. return expiration.before(new Date());
  58. } catch (Exception e) {
  59. return false;
  60. }
  61. }
  62. /**
  63. * 刷新令牌
  64. *
  65. * @param token 原令牌
  66. * @return 新令牌
  67. */
  68. public String refreshToken(String token) {
  69. String refreshedToken;
  70. try {
  71. Claims claims = getClaimsFromToken(token);
  72. claims.put("created", new Date());
  73. refreshedToken = generateToken(claims);
  74. } catch (Exception e) {
  75. refreshedToken = null;
  76. }
  77. return refreshedToken;
  78. }
  79. /**
  80. * 验证令牌
  81. *
  82. * @param token 令牌
  83. * @param userDetails 用户
  84. * @return 是否有效
  85. */
  86. public Boolean validateToken(String token, UserDetails userDetails) {
  87. String username = getUsernameFromToken(token);
  88. return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
  89. }
  90. /**
  91. * 从claims生成令牌
  92. *
  93. * @param claims 数据声明
  94. * @return 令牌
  95. */
  96. private String generateToken(Map<String, Object> claims) {
  97. Date expirationDate = new Date(System.currentTimeMillis() + expiration);
  98. return Jwts.builder().setClaims(claims)
  99. .setExpiration(expirationDate)
  100. .signWith(SignatureAlgorithm.HS512, secret)
  101. .compact();
  102. }
  103. /**
  104. * 从令牌中获取数据声明
  105. *
  106. * @param token 令牌
  107. * @return 数据声明
  108. */
  109. private Claims getClaimsFromToken(String token) {
  110. Claims claims;
  111. try {
  112. claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
  113. } catch (Exception e) {
  114. claims = null;
  115. }
  116. return claims;
  117. }
  118. }

 异常

创建一个exception包

  1. package com.xinzhi.exception;
  2. /**
  3. * jwt异常
  4. */
  5. public class CustomException extends RuntimeException {
  6. //异常错误编码
  7. private int code ;
  8. //异常信息
  9. private String message;
  10. private CustomException(){}
  11. public CustomException(int code, String message) {
  12. this.code = code;
  13. this.message = message;
  14. }
  15. public int getCode() {
  16. return code;
  17. }
  18. @Override
  19. public String getMessage() {
  20. return message;
  21. }
  22. }

service

JwtAuthService

  1. package com.xinzhi.service;
  2. import com.xinzhi.exception.CustomException;
  3. import com.xinzhi.utils.JwtTokenUtil;
  4. import org.springframework.security.authentication.AuthenticationManager;
  5. import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  6. import org.springframework.security.core.Authentication;
  7. import org.springframework.security.core.AuthenticationException;
  8. import org.springframework.security.core.context.SecurityContextHolder;
  9. import org.springframework.security.core.userdetails.UserDetails;
  10. import org.springframework.security.core.userdetails.UserDetailsService;
  11. import org.springframework.stereotype.Service;
  12. import javax.annotation.Resource;
  13. @Service
  14. public class JwtAuthService {
  15. @Resource
  16. private AuthenticationManager authenticationManager;
  17. @Resource
  18. private UserDetailsService userDetailsService;
  19. @Resource
  20. private JwtTokenUtil jwtTokenUtil;
  21. public UserDetails login(String username, String password) throws CustomException {
  22. try{
  23. // 通过手机号查到用户信息
  24. //使用用户名密码进行登录验证
  25. UsernamePasswordAuthenticationToken upToken =
  26. new UsernamePasswordAuthenticationToken(username, password);
  27. Authentication authentication = authenticationManager.authenticate(upToken);
  28. SecurityContextHolder.getContext().setAuthentication(authentication);
  29. }catch(AuthenticationException e){
  30. throw new CustomException(500, "用户名或密码不正确");
  31. }
  32. //返回了一个用户信息
  33. UserDetails userDetails = userDetailsService.loadUserByUsername( username );
  34. return userDetails;
  35. }
  36. public String refreshToken(String oldToken) {
  37. if (!jwtTokenUtil.isTokenExpired(oldToken)) {
  38. return jwtTokenUtil.refreshToken(oldToken);
  39. }
  40. return null;
  41. }
  42. }

Controller

  1. package com.xinzhi.controller;
  2. import com.google.code.kaptcha.impl.DefaultKaptcha;
  3. import com.xinzhi.model.vo.CaptchaImageVO;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RequestMethod;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import javax.annotation.Resource;
  8. import javax.imageio.ImageIO;
  9. import javax.servlet.ServletOutputStream;
  10. import javax.servlet.http.HttpServletResponse;
  11. import javax.servlet.http.HttpSession;
  12. import java.awt.image.BufferedImage;
  13. /**
  14. * 获取验证码
  15. */
  16. @RestController
  17. public class CaptchaController extends Throwable {
  18. @Resource
  19. DefaultKaptcha captchaProducer;
  20. /**
  21. * 获取验证码
  22. */
  23. @RequestMapping(value = "/kaptcha", method = RequestMethod.GET)
  24. public void kaptcha(HttpSession session, HttpServletResponse response) throws Exception {
  25. response.setDateHeader("Expires", 0);
  26. response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
  27. response.addHeader("Cache-Control", "post-check=0, pre-check=0");
  28. response.setHeader("Pragma", "no-cache");
  29. response.setContentType("image/jpeg");
  30. String capText = captchaProducer.createText();
  31. System.out.println(capText);
  32. CaptchaImageVO captchaImageVO = new CaptchaImageVO(capText,2 * 60);
  33. //将验证码存到session
  34. session.setAttribute("code", capText);
  35. //将图片返回给前端
  36. try(ServletOutputStream out = response.getOutputStream();) {
  37. BufferedImage bi = captchaProducer.createImage(capText);
  38. ImageIO.write(bi, "jpg", out);
  39. out.flush();
  40. }//使用try-with-resources不用手动关闭流
  41. }
  42. }

Filter

  1. package com.xinzhi.filter;
  2. import com.xinzhi.service.SpringUserDetailsService;
  3. import com.xinzhi.utils.JwtTokenUtil;
  4. import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  5. import org.springframework.security.core.Authentication;
  6. import org.springframework.security.core.context.SecurityContextHolder;
  7. import org.springframework.security.core.userdetails.UserDetails;
  8. import org.springframework.stereotype.Component;
  9. import org.springframework.util.StringUtils;
  10. import org.springframework.web.filter.OncePerRequestFilter;
  11. import javax.annotation.Resource;
  12. import javax.servlet.FilterChain;
  13. import javax.servlet.ServletException;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. import java.io.IOException;
  17. /**
  18. * jwt
  19. */
  20. @Component
  21. public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
  22. @Resource
  23. private SpringUserDetailsService myUserDetailsService;
  24. @Resource
  25. private JwtTokenUtil jwtTokenUtil;
  26. @Override
  27. protected void doFilterInternal(HttpServletRequest request,
  28. HttpServletResponse response,
  29. FilterChain filterChain)
  30. throws ServletException, IOException {
  31. String jwtToken = request.getHeader(jwtTokenUtil.getHeader());
  32. if(!StringUtils.isEmpty(jwtToken)){
  33. String username = jwtTokenUtil.getUsernameFromToken(jwtToken);
  34. Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  35. //如果可以正确的从JWT中提取用户信息,并且该用户未被授权
  36. if(username != null){
  37. UserDetails userDetails = myUserDetailsService.loadUserByUsername(username);
  38. if(jwtTokenUtil.validateToken(jwtToken,userDetails)){
  39. //给使用该JWT令牌的用户进行授权
  40. UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails,null, userDetails.getAuthorities());
  41. SecurityContextHolder.getContext().setAuthentication(authenticationToken);
  42. }
  43. }
  44. }
  45. filterChain.doFilter(request,response);
  46. }
  47. }

后台日志记录

依赖 已经有了

  1. <dependency>
  2. <groupId>org.aspectj</groupId>
  3. <artifactId>aspectjweaver</artifactId>
  4. <version>1.8.13</version>
  5. </dependency>

 代码

  1. package com.xinzhi.aspest;
  2. import com.xinzhi.model.User;
  3. import lombok.Data;
  4. import org.aspectj.lang.JoinPoint;
  5. import org.aspectj.lang.annotation.*;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.security.core.context.SecurityContextHolder;
  9. import org.springframework.stereotype.Component;
  10. import org.springframework.web.context.request.RequestContextHolder;
  11. import org.springframework.web.context.request.ServletRequestAttributes;
  12. import javax.servlet.http.HttpServletRequest;
  13. import java.util.Arrays;
  14. @Component("myAspect")
  15. @Aspect
  16. @Data
  17. public class MyAspect {
  18. // 打印日志
  19. private final static Logger logger= LoggerFactory.getLogger(MyAspect.class);
  20. //@Before指在切点方法之前执行,也就是在Controller层方法执行之前执行,这里可以通过JoinPoint获取一些有关方法的信息,在这里也可以修改参数的值
  21. //@Before()括号里设置的是切点方法的名称
  22. @Before("execution(public * com.xinzhi.controller..*.*(..))")
  23. public void doBefore(JoinPoint joinPoint) throws Throwable {
  24. // 接收到请求,记录请求内容
  25. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  26. HttpServletRequest request = attributes.getRequest();
  27. Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  28. if(principal.equals("anonymousUser")){
  29. logger.info("匿名用户");
  30. return;
  31. }
  32. User user =(User)(principal);
  33. // 记录下请求内容
  34. logger.info("URL : " + request.getRequestURI().toString());
  35. logger.info("HTTP_METHOD : " + request.getMethod());
  36. logger.info("IP : " + request.getRemoteAddr());
  37. logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
  38. logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
  39. logger.info("用户 : " + user.getUsername());
  40. }
  41. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/885755
推荐阅读
相关标签
  

闽ICP备14008679号