SpringSecurity

登录实战

前言

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

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

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

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

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

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

登录流程:

 

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

在使用Spring Security构建的Web应用程序中,登录流程涉及多个关键组件,下面详细介绍这些组件及其在登录过程中扮演的角色:

1. Filter Chain(过滤器链)
SecurityContextPersistenceFilter:维护安全上下文,确保线程间的安全信息传递。

UsernamePasswordAuthenticationFilter:负责处理基于表单的登录请求,收集用户名和密码,调用AuthenticationManager进行验证。

ConcurrentSessionFilter:管理并发会话,避免同一账号多处登录。

ExceptionTranslationFilter:捕获并处理认证或授权失败的异常。

FilterSecurityInterceptor:执行访问决策,依据用户权限判断是否允许访问特定资源。

2. AuthenticationManager(认证管理器)
负责处理认证请求,它接受一个Authentication对象(包含用户凭证),并返回一个经过完全填充的(已验证的或未经验证的)Authentication对象。对于基于用户名和密码的登录,Spring Security提供了DaoAuthenticationProvider,它使用UserDetailsService来检索用户信息。

3. UserDetailsService(用户详情服务)
接口定义了一个方法loadUserByUsername(String username),用于根据用户名加载用户信息。开发者需要实现这个接口,通常从数据库中查询用户信息。返回的是UserDetails对象,它包含用户的用户名、密码(通常是加密的)、权限等安全相关信息。

4. UserDetails(用户详情)
表示用户安全信息的核心接口,包含用户名、密码、账号是否过期、凭证是否过期、账号是否锁定以及赋予用户的权限集合。一个典型的实现是org.springframework.security.core.userdetails.User。

登录流程步骤(结合Spring Security组件):
请求登录页面:用户访问登录页面,该页面由Spring Security默认或自定义的登录页面处理。

提交登录信息:用户提交用户名和密码,这些信息通过UsernamePasswordAuthenticationFilter被封装成一个UsernamePasswordAuthenticationToken,并转发给AuthenticationManager。

验证用户凭证:

AuthenticationManager委托给配置的AuthenticationProvider(如DaoAuthenticationProvider)处理。

DaoAuthenticationProvider调用实现UserDetailsService的服务来加载用户详情(UserDetails)。

使用PasswordEncoder比较提交的密码与数据库中存储的密码哈希值,验证密码是否正确。

认证成功:

如果验证成功,AuthenticationProvider返回一个完全填充的Authentication对象,其中包含用户的角色和权限信息。

AuthenticationManager将此认证对象设置到安全上下文中,使得后续请求能够访问用户信息和权限。

通常会生成一个会话或JWT,并将其发送给客户端,用于后续请求的认证。

认证失败:

认证失败时,抛出异常,由ExceptionTranslationFilter捕获并处理,可能重定向到登录页面显示错误消息,或响应HTTP 401 Unauthorized。

访问控制:

用户携带令牌访问受保护资源时,FilterSecurityInterceptor基于用户的角色和权限进行访问决策,决定是否允许访问。

如上:Spring Security提供了一个强大且灵活的安全认证与授权框架,确保了登录过程的严谨性和安全性。

依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.xinzhi</groupId>
    <artifactId>SpringSecurityDemo2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringSecurityDemo2</name>
    <description>SpringSecurityDemo2</description>
    <properties>
        <java.version>11</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.6.2</spring-boot.version>
    </properties>
    <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.33</version>
        </dependency>

        <!-- 连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.16</version>
        </dependency>

        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--mybatis-plus的springboot支持-->
        <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>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
            <exclusions>
                <exclusion>
                    <artifactId>javax.servlet-api</artifactId>
                    <groupId>javax.servlet</groupId>
                </exclusion>
            </exclusions>
        </dependency>

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

        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

application.properties配置文件

# ????
spring.application.name=SpringSecurityDemo

# ???
server.port=8081

#-----------------------------Mysql??
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
#mybatis.config-location=classpath:mybatis-config.xml
mybatis-plus.type-aliases-package=com.xinzhi.model
mybatis-plus.mapper-locations=classpath:mapper/*.xml
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.configuration.map-underscore-to-camel-case=true


#-----------------------------Mysql
spring.datasource.url=jdbc:mysql://localhost:3306/jdb?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456

server.servlet.session.timeout=1m

建表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for menu
-- ----------------------------
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `pattern` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of menu
-- ----------------------------
INSERT INTO `menu` VALUES (1, '/admin/**');
INSERT INTO `menu` VALUES (2, '/user/**');
INSERT INTO `menu` VALUES (3, '/guest/**');

-- ----------------------------
-- Table structure for menu_role
-- ----------------------------
DROP TABLE IF EXISTS `menu_role`;
CREATE TABLE `menu_role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `rid` int(11) NULL DEFAULT NULL,
  `mid` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of menu_role
-- ----------------------------
INSERT INTO `menu_role` VALUES (1, 1, 1);
INSERT INTO `menu_role` VALUES (2, 1, 2);
INSERT INTO `menu_role` VALUES (3, 2, 2);
INSERT INTO `menu_role` VALUES (4, 3, 3);

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'product', '商品管理员');
INSERT INTO `role` VALUES (2, 'admin', '系统管理员');
INSERT INTO `role` VALUES (3, 'user', '用户管理员');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `enabled` tinyint(1) NULL DEFAULT NULL,
  `accountNonExpired` tinyint(1) NULL DEFAULT NULL,
  `accountNonLocked` tinyint(1) NULL DEFAULT NULL,
  `credentialsNonExpired` tinyint(1) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'root', '$2a$10$YeilFczw8sQ1RBthIFLRW.pI/KkdbDofOmZ0w5Wfq9mMMyL3ylC/.', 1, 1, 1, 1);
INSERT INTO `user` VALUES (2, 'admin', '$2a$10$YeilFczw8sQ1RBthIFLRW.pI/KkdbDofOmZ0w5Wfq9mMMyL3ylC/.', 1, 1, 1, 1);
INSERT INTO `user` VALUES (3, 'han', '$2a$10$YeilFczw8sQ1RBthIFLRW.pI/KkdbDofOmZ0w5Wfq9mMMyL3ylC/.', 1, 1, 1, 1);

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` int(11) NULL DEFAULT NULL,
  `rid` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 1, 2);
INSERT INTO `user_role` VALUES (3, 2, 2);
INSERT INTO `user_role` VALUES (4, 3, 3);

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接口

package com.xinzhi.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xinzhi.model.Menu;
import com.xinzhi.model.Role;
import com.xinzhi.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface UserMapper extends BaseMapper<User> {

    User selectByUsername(String username);

    List<Role> selectRolesByUserId(Integer id);

    Integer updatePassword(@Param("username") String username, @Param("password") String password);

    List<Menu> selectMenuByRoleIds(@Param("roleIds")List<Integer> roleIds);

}

usermapper

<?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.xinzhi.dao.UserMapper" >

    <sql id="Base_Column_List" >
        id, username, password, enabled, accountNonExpired, accountNonLocked, credentialsNonExpired
    </sql>
    <update id="updatePassword">
        update user set password=#{password} where username=#{username}
    </update>

    <select id="selectByUsername" resultType="com.xinzhi.model.User">
        select
        <include refid="Base_Column_List" />
        from user
        where username = #{username}
    </select>
    <select id="selectRolesByUserId" resultType="com.xinzhi.model.Role">
        select
            r.name,r.id,r.content
        from
            role r
                left join
            user_role ur
            on r.id = ur.rid
        where ur.uid=#{uid}
    </select>
    <select id="selectMenuByRoleIds" resultType="com.xinzhi.model.Menu">
        select
        m.id,m.pattern
        from menu m
        left join menu_role rm on m.id=rm.mid
        where
        <foreach collection="roleIds" open="rm.rid in(" item="id" close=")" separator=",">
            #{id}
        </foreach>
    </select>

</mapper>

Service层

package com.xinzhi.service;

import com.xinzhi.dao.UserMapper;
import com.xinzhi.model.Menu;
import com.xinzhi.model.Role;
import com.xinzhi.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

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

/**
 * 权限
 */
@Service
@Slf4j

public class SpringUserDetailsService implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.selectByUsername(username);
//        判断用户是否存在
        if (user == null){
            throw new UsernameNotFoundException("用户不存在");
        }
//       根据用户ID查找用户的角色列表
        List<Role> roles = userMapper.selectRolesByUserId(user.getId());
//      获取角色ID列表
        List<Integer> roleIds = roles.stream().map(s -> s.getId()).collect(Collectors.toList());
//      根据角色ID列表查找权限
        List<Menu> menus = userMapper.selectMenuByRoleIds(roleIds);
//     获取角色名称列表并加上前缀 "ROLE_"
        List<String> collect = roles.stream().map(s -> "ROLE_" + s.getName()).collect(Collectors.toList());
//     获取菜单权限模式
        List<String> collect1 = menus.stream().map(m -> m.getPattern()).collect(Collectors.toList());
//     合并菜单权限和角色权限
        Collections.addAll(collect1,collect.stream().toArray(String[] ::new));
//        将权限列表设置到用户对象中
        user.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",",collect1)));
//        返回包含用户详情的User对象
        return user;

    }
}

config配置类

package com.xinzhi.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xinzhi.filter.CaptchaCodeFilter;
import com.xinzhi.service.SpringUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CaptchaCodeFilter captchaCodeFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 表单认证
        http.formLogin()
                .loginProcessingUrl("/login")
//                .successForwardUrl("/index")
//                .failureForwardUrl("/fail") //登录失败跳转地址
                //认证成功处理
                .successHandler(((request, response, authentication) -> {
                    HashMap<String, Object> map = new HashMap<>();
                    map.put("msg", "认证成功");
                    map.put("status", 200);
                    map.put("用户信息", authentication.getPrincipal());
                    response.setContentType("application/json;charset=UTF-8");
                    String s = new ObjectMapper().writeValueAsString(map);
                    response.getWriter().println(s);
                }))
                // 认证失败处理
                .failureHandler(((request, response, exception) -> {
                    HashMap<String, Object> map = new HashMap<>();
                    map.put("msg", "认证失败");
                    map.put("status", 500);
                    map.put("异常信息", exception.getMessage());
                    response.setContentType("application/json;charset=UTF-8");
                    String s = new ObjectMapper().writeValueAsString(map);
                    response.getWriter().println(s);
                }))
                .loginPage("/login.html");

        // 拦截
        http.authorizeHttpRequests()
                .antMatchers("/login.html", "/kaptcha", "/authentication", "/refreshtoken").permitAll()
                .antMatchers("/fail.html").permitAll()    //fail.html 不需要被认证
                .antMatchers("/invalidSession.html").permitAll()
                .anyRequest().access((authenticationSupplier, requestAuthorizationContext) -> {
                    Collection<? extends GrantedAuthority> authorities = authenticationSupplier.get().getAuthorities();
                    String requestURI = requestAuthorizationContext.getRequest().getRequestURI();
                    SimpleGrantedAuthority simpleGrantedAuthority
                            = new SimpleGrantedAuthority(requestURI);
                    boolean contains = authorities.contains(simpleGrantedAuthority);
                    return new AuthorizationDecision(contains);
                });
//                .antMatchers("/user/detail").hasAuthority("/user/**")
//                .antMatchers("/index.html").hasRole("super")
//                .anyRequest().authenticated();

        // 异常处理
        http.exceptionHandling()
                .accessDeniedHandler(((request, response, accessDeniedException) -> {
                    HashMap<String, Object> map = new HashMap<>();
                    map.put("msg", "禁止访问");
                    map.put("status", 403);
                    map.put("异常信息", accessDeniedException.getMessage());
                    response.setContentType("application/json;charset=UTF-8");
                    String s = new ObjectMapper().writeValueAsString(map);
                    response.getWriter().println(s);
                }));
        // session管理
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                //.invalidSessionUrl("/invalidSession.html")    //非法超时session跳转页面
                .maximumSessions(1) // 允许登录的最多用户数量
                .maxSessionsPreventsLogin(false) // true表示已经登录就不予许再次登录, false表示允许再次登录但是之前的登录账户会被踢下线
                .expiredSessionStrategy(new CustomExpiredSessionStrategy());

        /**
         * 退出登录
         */
        http.logout().logoutSuccessHandler(((request, response, authentication) -> {
            HashMap<String, Object> map = new HashMap<>();
            map.put("msg", "退出成功");
            map.put("status", 200);
            response.setContentType("application/json;charset=UTF-8");
            String s = new ObjectMapper().writeValueAsString(map);
            response.getWriter().println(s);
        }));
//        验证码
        http.addFilterBefore(captchaCodeFilter, UsernamePasswordAuthenticationFilter.class);
//
        /**
         * 记住我
         */
        http.rememberMe().tokenValiditySeconds(7 * 24 * 3600);
//跨域
        http.csrf().disable();
//        跨域
//        http.cors(c -> {
//            CorsConfigurationSource source = request -> {
//                CorsConfiguration config = new CorsConfiguration();
//                config.setAllowedOrigins(Arrays.asList("*"));
//                config.setAllowedMethods(Arrays.asList("*"));
//                config.addAllowedHeader("*");
//                return config;
//            };
//            c.configurationSource(source);
//        });
// CSRF 跨站攻击
//        http.csrf()
//                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
//                .ignoringAntMatchers("/authentication");
    }



    /**
     * 注入加密类
     * @return
     */
    @Bean("passwordEncoder")
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Resource
    private SpringUserDetailsService userDetailsService;
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
        // 加密方式认证
        builder.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

}

Controller层

package com.xinzhi.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
@RequestMapping("/user")
public class TestController {

    @RequestMapping("/hello")
    @ResponseBody
    public String test(){
        return "security";
    }


    @RequestMapping("/index")
    public String index(){
        return "redirect:/index.html";
    }

    @PostMapping("/fail")
    public String fail(){
        return "redirect:/fail.html";
    }

    @GetMapping("/detail")

    public String detail(){
        return "detail";
    }

    @GetMapping("/add")
    @ResponseBody
    public String add(){
        return "add";
    }

    @GetMapping("/delete")
    @ResponseBody
    public String delete(){
        return "delete";
    }


    @GetMapping("/update")
    @ResponseBody
    public String update(){
        return "update";
    }

    @GetMapping("/select")
    @ResponseBody
    public String select(){
        return "select";
    }


}

页面

static

login.heml

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

templates

detail.html 退出页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="/logout" >退出</a>
详情页
</body>
</html>

fail.html 超时页面

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>error</title>
</head>
<body>
操作失败,请重新登录. <a href="/login.html">跳转</a>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  首页
</body>
</html>

测试(页面上)

http://localhost:8080/login.html

记住我

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

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

退出登录

退出页面有相对应的代码

配置文件也有对应的代码

验证码

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

<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
    <exclusions>
        <exclusion>
            <artifactId>javax.servlet-api</artifactId>
            <groupId>javax.servlet</groupId>
        </exclusion>
    </exclusions>
</dependency>

实体类

创建一个vo

CaptchaImageVO

package com.xinzhi.model.vo;

import lombok.Data;

import java.time.LocalDateTime;

/**
 * 验证码的实体类
 */
@Data
public class CaptchaImageVO {

    //验证码文字
    private String code;
    //验证码失效时间
    private LocalDateTime expireTime;

    public CaptchaImageVO(String code, int expireAfterSeconds){
        this.code = code;
        this.expireTime = LocalDateTime.now().plusSeconds(expireAfterSeconds);
    }

    //验证码是否失效
    public boolean isExpried() {
        return LocalDateTime.now().isAfter(expireTime);
    }

    public String getCode() {
        return code;
    }
}

kaptcha.properties配置文件

#????????
kaptcha.border=no
kaptcha.border.color=105,179,90
kaptcha.image.width=100
kaptcha.image.height=45
kaptcha.session.key=code
kaptcha.textproducer.font.color=blue
kaptcha.textproducer.font.size=35
kaptcha.textproducer.char.length=4
kaptcha.textproducer.font.names=??,??,????

配置类CaptchaConfig

package com.xinzhi.config;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.Properties;
//验证码的配置文件
@Component
@PropertySource(value = {"classpath:kaptcha.properties"})
public class CaptchaConfig {

    @Value("${kaptcha.border}")
    private String border;
    @Value("${kaptcha.border.color}")
    private String borderColor;
    @Value("${kaptcha.textproducer.font.color}")
    private String fontColor;
    @Value("${kaptcha.image.width}")
    private String imageWidth;
    @Value("${kaptcha.image.height}")
    private String imageHeight;
    @Value("${kaptcha.textproducer.char.length}")
    private String charLength;
    @Value("${kaptcha.textproducer.font.names}")
    private String fontNames;
    @Value("${kaptcha.textproducer.font.size}")
    private String fontSize;
    @Value("${kaptcha.session.key}")
    private String sessionKey;

    @Bean(name = "captchaProducer")
    public DefaultKaptcha getKaptchaBean() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border", border);
        properties.setProperty("kaptcha.border.color", borderColor);
        properties.setProperty("kaptcha.textproducer.font.color", fontColor);
        properties.setProperty("kaptcha.image.width", imageWidth);
        properties.setProperty("kaptcha.image.height", imageHeight);
        properties.setProperty("kaptcha.session.key", sessionKey);
        properties.setProperty("kaptcha.textproducer.char.length", charLength);
        properties.setProperty("kaptcha.textproducer.font.names", fontNames);
        properties.setProperty("kaptcha.textproducer.font.size",fontSize);
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

}

Controller层里

CaptchaController

​
​
package com.xinzhi.controller;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.xinzhi.model.vo.CaptchaImageVO;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;

/**
 * 获取验证码
 */
@RestController
public class CaptchaController extends Throwable {

    @Resource
    DefaultKaptcha captchaProducer;
    /**
     * 获取验证码
     */
    @RequestMapping(value = "/kaptcha", method = RequestMethod.GET)
    public void kaptcha(HttpSession session, HttpServletResponse response) throws Exception {

        response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("image/jpeg");

        String capText = captchaProducer.createText();
        System.out.println(capText);
        CaptchaImageVO captchaImageVO = new CaptchaImageVO(capText,2 * 60);
        //将验证码存到session
        session.setAttribute("code", capText);
        //将图片返回给前端
        try(ServletOutputStream out = response.getOutputStream();) {
            BufferedImage bi = captchaProducer.createImage(capText);
            ImageIO.write(bi, "jpg", out);
            out.flush();
        }//使用try-with-resources不用手动关闭流
    }
}

​

​

创建一个Filter包

package com.xinzhi.filter;

import com.alibaba.druid.util.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap;

/**
 * 验证码
 */
@Component
public class CaptchaCodeFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        // 必须是登录的post请求才能进行验证,其他的直接放行
        if(StringUtils.equals("/login",request.getRequestURI())
                && StringUtils.equalsIgnoreCase(request.getMethod(),"post")){

            //1.验证谜底与用户输入是否匹配
            if (!validate(new ServletWebRequest(request), response)){
                return;
            }
        }
        //通过校验,就放行
        filterChain.doFilter(request,response);

    }

    private Boolean validate(ServletWebRequest request,HttpServletResponse response) throws ServletRequestBindingException, IOException {

        HttpSession session = request.getRequest().getSession();
        //获取用户登录界面输入的captchaCode
        String codeInRequest = ServletRequestUtils.getStringParameter(
                request.getRequest(),"captchaCode");
        if(StringUtils.isEmpty(codeInRequest)){
            HashMap<String, Object> map = new HashMap<>();
            map.put("msg", "验证码不能为空");
            map.put("status", 500);
            response.setContentType("application/json;charset=UTF-8");
            String s = new ObjectMapper().writeValueAsString(map);
            response.getWriter().println(s);
            return false;
        }

        // 获取session池中的验证码谜底
        String codeInSession = (String)
                session.getAttribute("code");
        // 请求验证码校验
        if(!StringUtils.equals(codeInSession, codeInRequest)) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("msg", "验证码不匹配");
            map.put("status", 500);
            response.setContentType("application/json;charset=UTF-8");
            String s = new ObjectMapper().writeValueAsString(map);
            response.getWriter().println(s);
            return false;
        }
        return true;
    }
}

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

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

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

也有这段代码

整合JTW

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

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

 工具类

package com.xinzhi.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Data
@Component
public class JwtTokenUtil {
    private String secret="xinzhi";
    private Long expiration=3600000L;
    private String header="JWTHeaderName";


    /**
     * 生成token令牌
     *
     * @param userDetails 用户
     * @return 令token牌
     */
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>(2);
        //获取用户名
        claims.put("sub", userDetails.getUsername());
        // 创建时间
        claims.put("created", new Date());
        return generateToken(claims);
    }

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

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

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

    /**
     * 验证令牌
     *
     * @param token       令牌
     * @param userDetails 用户
     * @return 是否有效
     */
    public Boolean validateToken(String token, UserDetails userDetails) {
        String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }


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

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

}

 异常

创建一个exception包

package com.xinzhi.exception;

/**
 * jwt异常
 */

public class CustomException extends RuntimeException {
    //异常错误编码
    private int code ;
    //异常信息
    private String message;

    private CustomException(){}

    public CustomException(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }
}

service

JwtAuthService

package com.xinzhi.service;

import com.xinzhi.exception.CustomException;
import com.xinzhi.utils.JwtTokenUtil;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class JwtAuthService {

    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private JwtTokenUtil jwtTokenUtil;

    public UserDetails login(String username, String password) throws CustomException {
        try{

            // 通过手机号查到用户信息

            //使用用户名密码进行登录验证
            UsernamePasswordAuthenticationToken upToken =
                    new UsernamePasswordAuthenticationToken(username, password);
            Authentication authentication = authenticationManager.authenticate(upToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }catch(AuthenticationException e){
            throw new CustomException(500, "用户名或密码不正确");
        }

        //返回了一个用户信息
        UserDetails userDetails = userDetailsService.loadUserByUsername( username );
        return userDetails;

    }

    public String refreshToken(String oldToken) {
        if (!jwtTokenUtil.isTokenExpired(oldToken)) {
            return jwtTokenUtil.refreshToken(oldToken);
        }
        return null;
    }
}

Controller

package com.xinzhi.controller;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.xinzhi.model.vo.CaptchaImageVO;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;

/**
 * 获取验证码
 */
@RestController
public class CaptchaController extends Throwable {

    @Resource
    DefaultKaptcha captchaProducer;
    /**
     * 获取验证码
     */
    @RequestMapping(value = "/kaptcha", method = RequestMethod.GET)
    public void kaptcha(HttpSession session, HttpServletResponse response) throws Exception {

        response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("image/jpeg");

        String capText = captchaProducer.createText();
        System.out.println(capText);
        CaptchaImageVO captchaImageVO = new CaptchaImageVO(capText,2 * 60);
        //将验证码存到session
        session.setAttribute("code", capText);
        //将图片返回给前端
        try(ServletOutputStream out = response.getOutputStream();) {
            BufferedImage bi = captchaProducer.createImage(capText);
            ImageIO.write(bi, "jpg", out);
            out.flush();
        }//使用try-with-resources不用手动关闭流
    }
}

Filter

package com.xinzhi.filter;


import com.xinzhi.service.SpringUserDetailsService;
import com.xinzhi.utils.JwtTokenUtil;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * jwt
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Resource
    private SpringUserDetailsService myUserDetailsService;

    @Resource
    private JwtTokenUtil jwtTokenUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        String jwtToken = request.getHeader(jwtTokenUtil.getHeader());
        if(!StringUtils.isEmpty(jwtToken)){
            String username = jwtTokenUtil.getUsernameFromToken(jwtToken);
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            //如果可以正确的从JWT中提取用户信息,并且该用户未被授权
            if(username != null){
                UserDetails userDetails = myUserDetailsService.loadUserByUsername(username);
                if(jwtTokenUtil.validateToken(jwtToken,userDetails)){
                    //给使用该JWT令牌的用户进行授权
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails,null, userDetails.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
        }
        filterChain.doFilter(request,response);

    }
}

后台日志记录

依赖 已经有了

<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>

 代码

package com.xinzhi.aspest;

import com.xinzhi.model.User;
import lombok.Data;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

@Component("myAspect")
@Aspect
@Data
public class MyAspect {
//    打印日志

    private final static Logger logger= LoggerFactory.getLogger(MyAspect.class);

    //@Before指在切点方法之前执行,也就是在Controller层方法执行之前执行,这里可以通过JoinPoint获取一些有关方法的信息,在这里也可以修改参数的值

    //@Before()括号里设置的是切点方法的名称
    @Before("execution(public * com.xinzhi.controller..*.*(..))")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if(principal.equals("anonymousUser")){
            logger.info("匿名用户");
            return;
        }
        User user =(User)(principal);
        // 记录下请求内容
        logger.info("URL : " + request.getRequestURI().toString());
        logger.info("HTTP_METHOD : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
        logger.info("用户 : " + user.getUsername());
    }

}

  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值