精华内容
下载资源
问答
  • 2021-04-27 00:36:04

    之前写过一篇关于Springboot+Shiro实现前后端分离的权限管理系统。但是由于使用了框架,对于一些小系统,实在用不上Shiro,而且还要加上学习成本。今天就来用原生的Spring技术实现登录拦截。

    前后端分离

    要实现前后端分离,需要考虑以下2个问题: 1. 项目不再基于session了,如何知道访问者是谁? 2. 如何确认访问者的权限?

    前后端分离,一般都是通过token实现,本项目也是一样;用户登录时,生成token及 token过期时间,token与用户是一一对应关系,调用接口的时候,把token放到header或 请求参数中,服务端就知道是谁在调用接口。

    源码先双手奉上:https://github.com/FENGZHIJIE1998/Auth-demo

    各位客官觉得好用记得给个Star!

    项目架构图

    导包、配置文件都省略。

    第一步编写自己的拦截器AuthInteceptor

    public class AuthInterceptor implements HandlerInterceptor {

    @Autowired

    private AuthService authService;

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {

    String token = TokenUtil.getRequestToken(request);

    //如果token为空

    if (StringUtils.isBlank(token)) {

    setReturn(response,400,"用户未登录,请先登录");

    return false;

    }

    //1. 根据token,查询用户信息

    UserEntity userEntity = authService.findByToken(token);

    //2. 若用户不存在,

    if (userEntity == null) {

    setReturn(response,400,"用户不存在");

    return false;

    }

    //3. token失效

    if (userEntity.getExpireTime().isBefore(LocalDateTime.now())) {

    setReturn(response,400,"用户登录凭证已失效,请重新登录");

    return false;

    }

    return true;

    }

    @Override

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {

    }

    @Override

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

    }

    //返回错误信息

    private static void setReturn(HttpServletResponse response, int status, String msg) throws IOException {

    HttpServletResponse httpResponse = (HttpServletResponse) response;

    httpResponse.setHeader("Access-Control-Allow-Credentials", "true");

    httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());

    //UTF-8编码

    httpResponse.setCharacterEncoding("UTF-8");

    response.setContentType("application/json;charset=utf-8");

    Result build = Result.build(status, msg);

    String json = JSON.toJSONString(build);

    httpResponse.getWriter().print(json);

    }

    }

    第二步,将拦截器配置进Spring

    @Configuration

    public class InterceptorConfig implements WebMvcConfigurer {

    @Bean

    public AuthInterceptor authInterceptor() {

    return new AuthInterceptor();

    }

    @Override

    public void addInterceptors(InterceptorRegistry registry) {

    // 放行路径

    List patterns = new ArrayList();

    patterns.add("/webjars/**");

    patterns.add("/druid/**");

    patterns.add("/sys/login");

    patterns.add("/swagger/**");

    patterns.add("/v2/api-docs");

    patterns.add("/swagger-ui.html");

    patterns.add("/swagger-resources/**");

    patterns.add("/login");

    registry.addInterceptor(authInterceptor()).addPathPatterns("/**")

    .excludePathPatterns(patterns);

    }

    }

    第三步:测试

    Controller 层

    /**

    * 登录校验

    */

    @RestController("/")

    public class AuthController {

    @Autowired

    private AuthService authService;

    /**

    * 登录

    *

    * @param loginDTO

    * @return token登录凭证

    */

    @PostMapping("/login")

    public Result login(@RequestBody LoginDTO loginDTO) {

    String username = loginDTO.getUsername();

    String password = loginDTO.getPassword();

    //用户信息

    UserEntity user = authService.findByUsername(username);

    //账号不存在、密码错误

    if (user == null || !user.getPassword().equals(password)) {

    return Result.build(400, "用户名或密码错误");

    } else {

    //生成token,并保存到数据库

    String token = authService.createToken(user);

    TokenVO tokenVO = new TokenVO();

    tokenVO.setToken(token);

    return Result.ok(tokenVO);

    }

    }

    /**

    * 登出

    *

    * @param

    * @return

    */

    @PostMapping("/logout")

    public Result logout(HttpServletRequest request) {

    //从request中取出token

    String token = TokenUtil.getRequestToken(request);

    authService.logout(token);

    return Result.ok();

    }

    /**

    * 测试

    *

    * @param

    * @return

    */

    @PostMapping("/test")

    public Result test( ) {

    return Result.ok("恭喜你,验证成功啦,我可以返回数据给你");

    }

    }

    编写Service层

    @Service

    public class AuthServiceImpl implements AuthService {

    @Autowired

    private UserRepository userRepository;

    @Override

    public UserEntity findByUsername(String username) {

    return userRepository.findByUsername(username);

    }

    //12小时后失效

    private final static int EXPIRE = 12;

    @Override

    public String createToken(UserEntity user) {

    //用UUID生成token

    String token = UUID.randomUUID().toString();

    //当前时间

    LocalDateTime now = LocalDateTime.now();

    //过期时间

    LocalDateTime expireTime = now.plusHours(EXPIRE);

    //保存到数据库

    user.setLoginTime(now);

    user.setExpireTime(expireTime);

    user.setToken(token);

    userRepository.save(user);

    return token;

    }

    @Override

    public void logout(String token) {

    UserEntity userEntity = userRepository.findByToken(token);

    //用UUID生成token

    token = UUID.randomUUID().toString();

    userEntity.setToken(token);

    userRepository.save(userEntity);

    }

    @Override

    public UserEntity findByToken(String token) {

    return userRepository.findByToken(token);

    }

    }

    然后启动项目即可

    首先数据库保存一个用户,用户管理这里就不做啦。

    我们来看看效果:使用Swagger查看效果

    未登录状态下:

    传递错误的token:

    登录成功:

    传递正确的token:

    token过期之后

    郑州不孕不育医院:http://www.xasgnk.com/

    总结:如果你看过我那篇关于Shiro前后端分离的架构,其实很容易发现,这不就是简化版的Shiro吗?拦截、验证、返回错误信息。这就是思想上的一致。Shiro底层是用Filter(过滤器)、HandlerInterceptor底层是Interceptor(拦截器)。此外,本篇不仅仅可以实现登录拦截,对于权限拦截也是妥妥的,只需要再写一个拦截器,对权限进行验证,这里就不展开了。最主要还是前后端分离的思想,由前端控制路由,后端只负责返回对应信息,这肯定是发展趋势。

    更多相关内容
  • 目录 前言 一、准备工作 1.1、设计数据库(我的工程目录中的sql文件夹下有sql文件直接导入...2.3、自定义登录验证结果、登出结果、无权访问处理器 2.3.1、自定义登录成功处理器 2.3.2、自定义登录失败处理器 2...

    目录

    前言

    一、准备工作

    1.1、设计数据库(我的工程目录中的sql文件夹下有sql文件直接导入即可)

    二、代码实现

    2.1、数据操作

    2.2、自定义登录逻辑

    2.2.1、创建自定义UserDetailsService

    2.2.2、自定义的密码加密类

    2.3、自定义登录验证结果、登出结果、无权访问处理器

    2.3.1、自定义登录成功处理器

    2.3.2、自定义登录失败处理器

    2.3.3、自定义退出成功处理器

    2.3.4、自定义无权访问处理器

    2.4、WebSecurityConfig配置

    2.4.1、配置security

    2.4.2、内置访问控制方法介绍

    2.4.3、角色权限判断

    2.5、编写测试用例

    2.5.1、简单的json返回体

    2.5.2、测试Controller类

    三、测试


    前言

    本文基于spring boot +mybatis+spring security+postname 实现下面功能并进行测试。

    主要实现功能:

    1、前后端分离用户登录验证

    2、实现用户权限控制

    项目已经打包上传到github,下载地址https://github.com/zzqgit/SpringSecurityDemo.git

    一、准备工作

    1.1、设计数据库我的工程目录中的sql文件夹下有sql文件直接导入即可

     初始化数据如下:

    --user表
    INSERT INTO `security`.`user` (`id`, `username`, `password`) VALUES ('1', 'admin', '123456');
    INSERT INTO `security`.`user` (`id`, `username`, `password`) VALUES ('2', 'user1', '123456');
    INSERT INTO `security`.`user` (`id`, `username`, `password`) VALUES ('3', 'user2', '123456');
    
    --role表
    INSERT INTO `security`.`role` (`id`, `role_name`) VALUES ('1', 'admin');
    INSERT INTO `security`.`role` (`id`, `role_name`) VALUES ('2', 'user');
    
    --user_role_relation表
    INSERT INTO `security`.`user_role_relation` (`id`, `user_id`, `role_id`) VALUES ('1', '1', '1');
    INSERT INTO `security`.`user_role_relation` (`id`, `user_id`, `role_id`) VALUES ('2', '2', '2');
    INSERT INTO `security`.`user_role_relation` (`id`, `user_id`, `role_id`) VALUES ('3', '3', '2');
    
    --peimission表
    INSERT INTO `security`.`permission` (`id`, `authority`) VALUES ('1', 'insert');
    INSERT INTO `security`.`permission` (`id`, `authority`) VALUES ('2', 'update');
    INSERT INTO `security`.`permission` (`id`, `authority`) VALUES ('3', 'select');
    
    --role_peimission_relation表
    INSERT INTO `security`.`role_permission_relation` (`id`, `role_id`, `permissin_id`) VALUES ('6', '1', '1');
    INSERT INTO `security`.`role_permission_relation` (`id`, `role_id`, `permissin_id`) VALUES ('7', '1', '2');
    INSERT INTO `security`.`role_permission_relation` (`id`, `role_id`, `permissin_id`) VALUES ('8', '1', '3');
    INSERT INTO `security`.`role_permission_relation` (`id`, `role_id`, `permissin_id`) VALUES ('9', '2', '2');
    INSERT INTO `security`.`role_permission_relation` (`id`, `role_id`, `permissin_id`) VALUES ('10', '2', '3');
    INSERT INTO `security`.`role_permission_relation` (`id`, `role_id`, `permissin_id`) VALUES ('11', '3', '3');
    
    

    1.2、pom.xml依赖

    <?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>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.0.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.security</groupId>
        <artifactId>demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>demo</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web-services</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.0</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <!--自动装配 依赖包 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
        </dependencies>
    
        <build>
            <finalName>test</finalName>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
    
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    1.3、编辑application.yml配置文件

    server:
      port: 8090
    spring:
    
      datasource:
        url: jdbc:mysql://127.0.0.1/security?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
        username: root
        password: 123456
        driver-class-name: com.mysql.cj.jdbc.Driver
    
    mybatis:
      mapper-locations: classpath:mapping/*.xml
      type-aliases-package: com.security.demo.Entity
    
    

    二、代码实现

    2.1、数据操作

    这部分代码实现了对用户和用户权限查询的基本操作,学过mybatis的都应该知道,就不过多解释!

    2.1.1、用户实体类(User.java)

    public class User {
        private Integer id;
        private String username;
        private String password;
    
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
    }

     2.1.2、UserMapper

    @Mapper
    @Component(value = "UserMapper")
    public interface UserMapper {
    
        User findByName( String username);
    
    }

    2.1.3、UserService

    @Service
    public class UserService {
    
        @Autowired
        private UserMapper userMapper;
    
        public User findByName(String username){
            return userMapper.findByName(username);
        }
    }
    

    2.1.4、UserMapping.xml

    <?xml version="1.0" encoding="utf-8" ?>
            <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.security.demo.Mapper.UserMapper">
    
    <select id="findByName" resultType="com.security.demo.Entity.User">
            select * from user where username = #{username}
    </select>
    
    </mapper>
    

    2.1.5、用户权限实体类(Permission.java)

    public class Permission {
        private Integer id;
        private String authority;
    
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getAuthority() {
            return authority;
        }
        public void setAuthority(String authority) {
            this.authority = authority;
        }
    }
    

    2.1.6、用户角色实体类

    public class Role {
        private Integer id;
        private String role_name;
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getRole_name() {
            return role_name;
        }
        public void setRole_name(String role_name) {
            this.role_name = role_name;
        }
    }

    2.1.7、PermissionMapper

    @Mapper
    @Component(value = "PermissionMapper")
    public interface PermissionMapper {
    
        List<Permission> selectListByUserId(Integer id);
        List<Role> selectRoleListByUserId(Integer id);
    }

    2.1.8、PermissionService

    import java.util.List;
    
    @Service
    public class PermissionService {
    
        @Autowired
        private PermissionMapper permissionMapper;
    
        public List<Permission> selectListByUserId(Integer id){
            return permissionMapper.selectListByUserId(id);
        }
        public List<Role> selectRoleListByUserId(Integer id){
            return permissionMapper.selectRoleListByUserId(id);
        }
    }

    2.1.9、PermissionMapping.xml

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.security.demo.Mapper.PermissionMapper">
        <!-- 根据用户id查询用户所有的权限-->
        <select id="selectListByUserId" resultType="com.security.demo.Entity.Permission">
            SELECT
    p.authority
    FROM
    `user` AS u
    INNER JOIN user_role_relation AS ur ON u.id = ur.user_id
    INNER JOIN role AS r ON r.id = ur.role_id
    INNER JOIN role_permission_relation AS rp ON r.id = rp.role_id
    INNER JOIN permission as p ON rp.permissin_id = p.id
    WHERE u.id = #{id}
        </select>
    
        <!-- 根据用户id查询用户属于什么角色-->
        <select id="selectRoleListByUserId" resultType="com.security.demo.Entity.Role">
            SELECT
    r.role_name
    FROM
    `user` AS u
    INNER JOIN user_role_relation AS ur ON u.id = ur.user_id
    INNER JOIN role AS r ON r.id = ur.role_id
    WHERE u.id =#{id}
    
        </select>
    
    </mapper>
    

    2.2、自定义登录逻辑

    在实际项目中账号和密码都是从数据库中查询出来的。所以我们要通过自定义逻辑控制认证逻辑,只需要实现 UserDetailsService 接口即可。

    2.2.1、创建自定义UserDetailsService

    这是实现自定义用户认证的核心逻辑,loadUserByUsername(String username)的参数就是登录时提交的用户名,返回类型是一个叫UserDetails 的接口,需要在这里构造出他的一个实现类User,这是Spring security提供的用户信息实体。

    package com.security.demo.Security.custom;
    import com.security.demo.Entity.Permission;
    import com.security.demo.Entity.Role;
    import com.security.demo.Entity.User;
    import com.security.demo.Service.PermissionService;
    import com.security.demo.Service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    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.ArrayList;
    import java.util.List;
    
    @Service
    public class CustomUserDetailsService implements UserDetailsService {
        @Autowired
        private UserService userService;
        @Autowired
        private PermissionService permissionService;
    
        /**
         * 认证过程中 - 根据登录信息获取用户详细信息
         *
         * @param username 登录用户输入的用户名
         * @return
         * @throws UsernameNotFoundException
         */
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            //根据用户输入的用户信息,查询数据库中已注册用户信息
            User user = userService.findByName(username);
            //如果用户不存在直接抛出UsernameNotFoundException异常
            if (user == null) throw new UsernameNotFoundException("用户不存在");
            System.out.println(username);
            //声明一个用于存放用户权限的列表
            List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
            //获取该用户所拥有的权限
            List<Permission> authority = permissionService.selectListByUserId(user.getId());
            //获取该用户所属角色
            List<Role> role = permissionService.selectRoleListByUserId(user.getId());
            //把用户所拥有的权限添加到列表中
            authority.forEach(permission -> {
                grantedAuthorities.add(new SimpleGrantedAuthority(permission.getAuthority()));
            });
            //把用户角色加到列表中
            role.forEach(role1 -> {
                //注意:添加角色的时候要在前面加ROLE_前缀
                grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_"+role1.getRole_name()));
            });
    
            //创建并返回User对象,注意这里的User不是我们实体类里面的User
            return new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(), grantedAuthorities );
        }
    }
    

    2.2.2、自定义的密码加密类

    @Component
    public class CustomPasswordEncoder implements PasswordEncoder {
        @Override
        public String encode(CharSequence rawPassword) {
            return rawPassword.toString();
        }
    
        @Override
        public boolean matches(CharSequence rawPassword, String encodedPassword) {
            return encodedPassword.equals(rawPassword.toString());
        }
    }

    2.3、自定义登录验证结果、登出结果、无权访问处理器

    Spring Security为我们封装好了登录、登出的接口。默认登入路径:/login,登出路径:/logout。当然我们可以也修改默认的名字。登录成功、失败和登出的后续处理逻辑如何编写会在下面解释。
    当登录成功或登录失败都需要返回统一的json返回体给前台,前台才能知道对应的做什么处理。
    而实现登录成功和失败的异常处理需要分别实现AuthenticationSuccessHandler和AuthenticationFailureHandler接口并在WebSecurityConfig中注入,然后在configure(HttpSecurity http)方法中然后声明,WebSecurityConfig配置后面慢慢解释。

    2.3.1、自定义登录成功处理器

    /**
     * 自定义验证成功处理器
     * @author
     *
     */
    @Component
    public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            httpServletResponse.setContentType("application/json;charset=utf-8");
            PrintWriter out = httpServletResponse.getWriter();
            out.write("{\"status\":\"success\",\"msg\":\"登录成功\"}");
            out.flush();
            out.close();
        }
    }

    2.3.2、自定义登录失败处理器

    /**
     * 自定义验证失败处理器
     * @author
     *
     */
    @Component
    public class CustomAuthenticationFailHandler implements AuthenticationFailureHandler {
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            httpServletResponse.setContentType("application/json;charset=utf-8");
            PrintWriter out = httpServletResponse.getWriter();
            out.write("{\"status\":\"error\",\"msg\":\"登录失败\"}");
            out.flush();
            out.close();
        }
    
    }

    2.3.3、自定义退出成功处理器

    /**
     * 退出登录成功的处理
     */
    @Component
    public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
        @Override
        public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            httpServletResponse.setContentType("application/json;charset=utf-8");
            PrintWriter out = httpServletResponse.getWriter();
            out.write("{\"status\":\"success\",\"msg\":\"退出成功\"}");
            out.flush();
            out.close();
        }
    }

    2.3.4、自定义无权访问处理器

    /**
     * 自定义无权访问处理器
     */
    @Component
    public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response,
                           AccessDeniedException accessDeniedException) throws IOException, ServletException {
            
            //HttpServletResponse.SC_FORBIDDEN代表 403
            //response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.setHeader("Content-Type","application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.write("{\"status\":\"error\",\"msg\":\"权限不足!\"}");
            out.flush();
            out.close();
        }
    
    }

    2.4、WebSecurityConfig配置

    看代码接注释吧!

    2.4.1、配置security

    package com.security.demo.Security;
    
    import com.security.demo.Security.custom.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private CustomUserDetailsService customUserDetailsService;
        @Autowired
        private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
        @Autowired
        private CustomAuthenticationFailHandler customAuthenticationFailHandler;
        @Autowired
        private CustomAccessDeniedHandler customAccessDeniedHandler;
        @Autowired
        private CustomLogoutSuccessHandler customLogoutSuccessHandler;
    
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
            //将自定的CustomUserDetailsService装配到AuthenticationManagerBuilder
            auth.userDetailsService(customUserDetailsService).passwordEncoder(new CustomPasswordEncoder());
        }
    
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    
            http
    
                    /*匿名请求:不需要进行登录拦截的url*/
                    .authorizeRequests()
                     .antMatchers("/hello").permitAll() //允许任何人访问
                    .antMatchers("/admin").hasRole("admin")//当用户的角色是为admin时可以访问这个目录
                    .antMatchers("/getUser").hasAuthority("select") //当用户具有select权限时才可以访问这个方法
                    .anyRequest().authenticated()//其他的路径都是登录后才可访问
                    .and()
                    /*登录配置*/
                    .formLogin()
                    .loginPage("/login_page")//登录页,当未登录时会重定向到该页面
                    .successHandler(customAuthenticationSuccessHandler)//登录成功处理
                    .failureHandler(customAuthenticationFailHandler)//登录失败处理
                    .loginProcessingUrl("/login")//前端登录请求地址
                    .usernameParameter("username")//默认的用户名参数
                    .passwordParameter("password")//默认的密码参数
                    .permitAll()
                    .and()
                    /*登出配置*/
                    .logout()
                    .permitAll()
                    .logoutSuccessHandler(customLogoutSuccessHandler) //退出处理
                    .and()
                    .exceptionHandling()
                    .accessDeniedHandler(customAccessDeniedHandler)  //无权限时的处理
                    .and()
                    .cors() //跨域
                    .and()
                    //关闭csrf防护,类似于防火墙,不关闭上面的设置不会真正生效。
                    .csrf().disable();
        }
    
        //密码加密配置
        @Bean
        public PasswordEncoder passwordEncoder() {
    
            return new BCryptPasswordEncoder();
        }
    
    }
    
    

    2.4.2、内置访问控制方法介绍

    刚才在上面的配置类中我们也看到了.antMatchers("/hello").permitAll()配置之后/hello接口就何人都可以访问,

    类似这种控制方法security一共定义了6种访问控制方法:

    permitAll()

    表示所匹配的URL 任何人都允许访问

    denyAll()

    表示所匹配的URL 都不允许被访问

    anonymous()

    表示可以匿名访问匹配的URL。和permitAll()效果类似

    authenticated()

    表示所匹配的URL 都需要被认证才能访问

    fullyAuthenticated()

    如果用户不是被remember me 的,才可以访问

    rememberMe()

    被“remember me”的用户允许访问

     

     

    2.4.3、角色权限判断

    除了之前讲的内置权限控制 , Spring Security 中还支持很多其他权限控制。
    这些方法一般都用于用户已经被认证后,判断用户是否具有特定的权限或角色。

    就比如我们在上面的配置中的

    .antMatchers("/admin").hasRole("admin")//当用户的角色是为admin时可以访问这个目录

    .antMatchers("/getUser").hasAuthority("select") //当用户具有select权限时才可以访问这个方法

    1、hasAuthority(String)

    判断用户是否具有特定的权限,用户的权限是在自定义登录逻辑中创建User 对象时指定的。

    2、hasAnyAuthority(String …)

    如果用户具备给定权限中某一个,就允许访问。

    .antMatchers("/hello").hasAnyAuthority("adMin","admiN")
    

    3、hasRole(String)

    如果用户具备给定角色就允许访问。
    参数取值来源于自定义登录逻辑UserDetailsService 实现类中创建User 对象时给User 赋予的授权。

    在给用户赋予角色时角色需要以:ROLE_ 开头,后面添加角色名称。
    例如:ROLE_abc 其中abc 是角色名,ROLE_是固定的字符开头。

    在使用的时候可以直接不用加ROLE_

    4、hasAnyRole

    如果用户具备给定角色的任意一个,就允许被访问

    例如:

    .antMatchers("/hello").hasAnyRole("abC","abc","ABC")        //判断用户是否用户具备给定角色的任意一个,是就允许被访问

    5、hasIpAddress(String)

    如果请求是指定的IP 就可以访问。

    .antMatchers("/main.html").hasIpAddress("127.0.0.1")            //如果请求是指定的IP 就运行访问。
    

    除了在webSecurityConfig配置文件中声明某个接口需要特定的权限,我们还可以在Controller类中声明,但前提是在配置类中加上@EnableGlobalMethodSecurity(prePostEnabled = true)注解。我已经在上面配置类中加入了,关于使用请继续看下面2.5.2的代码。

    2.5、编写测试用例

    2.5.1、简单的json返回体

    package com.security.demo.Security.utils;
    import lombok.Data;
    @Data
    public class Response {
        private String code;
        private String msg;
        private Object data;
        public Response() {
            this.code = "200";
            this.msg = "SUCCESS";
        }
        public Response(String code, String msg){
            this.code = code;
            this.msg = msg;
        }
    }

    2.5.2、测试Controller类

    
    import com.security.demo.Entity.User;
    import com.security.demo.Security.utils.Response;
    import com.security.demo.Service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class DemoController {
    
        @Autowired
        private UserService userService;
        
        @RequestMapping("/hello")
        @ResponseBody
        public Response hello(){
            return new Response("200","hello!");
        }
        //该方法我们在security配置类中指定了admin角色才可以访问
        @RequestMapping("/admin")
        @ResponseBody
        public Response admin(){
            return new Response("200","admin!");
        }
        //当用户具有select权限时才可以访问该方法
        @PreAuthorize("hasAuthority('select')")
        @RequestMapping("/select")
        @ResponseBody
        public Response select(){
            return new Response("200","select");
        }
        //当用户具有insert权限时才可以访问该方法
        @PreAuthorize("hasAuthority('insert')")
        @RequestMapping("/insert")
        @ResponseBody
        public Response insert(){
            return new Response("200","insert");
        }
        //当用户具有update权限时才可以访问该方法
        @PreAuthorize("hasAuthority('update')")
        @RequestMapping("/update")
        @ResponseBody
        public Response update(){
            return new Response("200","update");
        }
        //如果访问需要登录的接口,如果用户还没登录就会跳转到这个接口
        @RequestMapping("/login_page")
        @ResponseBody
        public Response root(){
            Response response = new Response("-200","未登录!");
            return response;
        }
    
        @RequestMapping("/getUser")
        @ResponseBody
        public User getUser(){
            //获取我们正在登陆的用户信息
            //注意这里的User是security的
            org.springframework.security.core.userdetails.User userDetails = (org.springframework.security.core.userdetails.User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            //这里的User才是我们实体类里面的
            User user = userService.findByName(userDetails.getUsername());
            return user;
        }
    }
    

    编辑好之后继续干菜我们上面说到的通过使用@EnableGlobalMethodSecurity(prePostEnabled = true)注解就可以直接在控制类中通过加入@PreAuthorize等注解就可以对方法实现用户权限或角色判断。

    @PreAuthorize:在方法执行之前执行进行判断

    使用示例:

    @PreAuthorize("hasAuthority('select')")   判断用户是否具有select权限

    @PreAuthorize("hasRole('admin')") 判断用户是否具有admin角色身份

     

    @PostAuthorize:在方法执行之后执行

    三、测试

    在测试前先说一下,用浏览器来测试需要登录的接口是不行的,它会直接跳转到login_page,然后这个接口只是返回未登录的提示,此时你想在浏览器发起/login?username=user1&password=123456类似这样的登录请求是不行的,因为security的/login接口默认是POST请求,我们浏览器发起的是GET请求所以不行,当然你用js来请求,改下请求类型就行了,我这里用postname这个软件来测试。地址:https://www.getpostman.com/     下载下来安装就能用了。

    1、测试hello接口,不登录的情况下是可以访问的

    2、测试getUser接口,该接口是需要登录后才能访问的

     我们可以登录之后再进行访问,我们用user1账号来登录

    使用错误密码登录:

    使用正确密码登录:

     这时再进行访问就不会提示未登录了

     

     3、无角色权限测试,admin接口是需要具有admin角色身份才可以访问的。

    我们直接用上面登录的user1来测:

     会提示我们权限不足,接下来再用admin账号去登录再进行测试会的得到如下结果:

     4、用户权限测试

    根据我们最数据库中初始化的数据,user1、user2这个两个用户对应的user角色是没有insert权限的,我们接下来登录user1来测试一下:

     很明显是合理的。好了到这里就结束了,项目代码我已经传到githun,可直接下载

    地址:https://github.com/zzqgit/SpringSecurityDemo.git

     

     

    展开全文
  • 这边的拦截器,对应于spring MVC中的filter,所有的http请求,通过拦截器处理之后才能访问到对应的代码/资源。 最典型的应用场景就是实现访问权限控制,给予不同的用户/用户组不同的页面和接口访问权限,仅能够...
  • //在执行目标之前 判断用户是否登录,并封装 @Component public class LoginInterceptor implements HandlerInterceptor { StringRedisTemplate stringRedisTemplate = SpringUtil.getBean(StringRedisTemplate....
    package com.wms.controller;
    
    import cn.hutool.extra.spring.SpringUtil;
    import com.wms.exeception.MyException;
    import com.wms.utils.getUserConfig;
    import com.wms.vo.ResultCode;
    import com.wms.vo.ResultVo;
    import com.wms.vo.userVo;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.util.WebUtils;
    
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    //在执行目标之前 判断用户是否登录,并封装
    
    @Component
    public class LoginInterceptor implements HandlerInterceptor {
    
    
    
        StringRedisTemplate stringRedisTemplate = SpringUtil.getBean(StringRedisTemplate.class);
    
    
    
    
    
        //创建一个静态的threadLocal
        public static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
    
        //目标方法执行之前
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler ) throws Exception {
    
            getUserConfig getUserConfig = new getUserConfig();
            userVo userVo = getUserConfig.getUser(request);
            String user = userVo.getUser();
            String token =String.valueOf(stringRedisTemplate.opsForHash().get(user, "token")) ;
            if (token==null||"null".equals(token)){
                throw new MyException(new ResultVo(ResultCode.INVALID_REQUEST),ResultCode.INVALID_REQUEST.getMsg());
    
            }
    
            return true;
        }
    
        /**
         * 执行完毕之后分配临时用户让浏览器保存
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            //TODO  如果有需要浏览器保存的cookie信息
            Cookie cookie = new Cookie("key-name", "info");
            // 设置这个cookie作用域 过期时间
            cookie.setDomain("mall.com");
            cookie.setMaxAge(10);
            response.addCookie(cookie);
        }
    }
    
    
    package com.wms.controller;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    /**
     * @Author: suweiye
     * @Description: //TODO
     * @Date: 2022-05-28 16:28
     **/
    @Configuration
    public class Interceptor implements WebMvcConfigurer {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
              registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/login");
    
        }
    }
    
    package com.wms.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.filter.CorsFilter;
    
    /**
     * @Author: suweiye
     * @Description: //TODO
     * @Date: 2022-05-31 11:15
     **/
    @Component
    public class CorsConfig {
        @Bean
        CorsFilter corsFilter(){
            CorsConfiguration config = new CorsConfiguration();
    
            // Possibly...
            // config.applyPermitDefaultValues()
    
            config.setAllowCredentials(true);
            config.addAllowedOrigin("*");
            config.addAllowedHeader("*");
            config.addAllowedMethod("*");
    
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", config);
    
            return new CorsFilter(source);
        }
    }
    
    

    controller还需要加个跨域注解
    @CrossOrigin

    展开全文
  • 2、使用localStorage实现会话跟踪技术 1、前端发起登录请求 2、后端接受并处理请求 3、配置前端前置拦截器 4、后端配置拦截器 5、配置前端后置拦截器 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求...

    目录

    1、什么是localStorage和sessionStorage?

    2、使用localStorage实现会话跟踪技术

            1、前端发起登录请求

            2、后端接受并处理请求

            3、配置前端前置拦截器

            4、后端配置拦截器

            5、配置前端后置拦截器


            会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。服务器需要用来识别请求是否来自同一个浏览器 服务器用来识别浏览器的过程,这个过程就是会话跟踪 服务器识别浏览器后就可以在同一个会话中多次请求之间来共享数据。

    1、什么是localStorage和sessionStorage?

            这两是web Storage 的其中两个,是HTML 5引入的一个重要的功能,在前端开发的过程中会经常用到,它可以在客户端本地存储数据,类似cookie,但其功能却比cookie强大的多。cookie的大小只有4Kb左右(浏览器不同,大小也不同),而web Storage的大小有5MB。

    localStorage和sessionStorage都是保存在浏览器中,它们最主要的区别是:

    生命周期不同:sessionStorage类似于session作用域为一次会话(从打开浏览器到关闭浏览器)

    localStorage类似于cookie持久化存储在本地。

    1、localStorage创建存储数据后,除非手动删除数据,不然都会一直存储在浏览器中

    2、sessionStorage创建存储数据后,页面刷新、同源打开其他页面,代码跳转能够访问到当前的

    3、seesionStorage。如果关闭浏览器,sessionStorage就会消失。实际上我们可以理解成一种会话机制。

    提供的方法有以下几种:

    • setItem(key,value):设置storage中的键值对,存储值
    • getItem(key):获取storage中的键值对
    • removeItem(key):删除storage中的键值对
    • clear():清楚storage中的内容

            可以将对象以JSON字符串的格式存储在Storage中,可以使用JSON.stringify();将对象转化为JSON字符串。需要使用storage中的值的时候先使用getItem(key)来获取storage中的JSON字符串,然使用JSON.parse();将JSON字符串解析为json对象,这样就可以获取对象中的值了。

    2、使用localStorage实现会话跟踪技术

    登录流程:

        1、首次登录时,后端服务器判断用户账号密码正确之后,根据用户id、用户名、定义好的秘钥、过期时间生成 token ,返回给前端;
        2、前端拿到后端返回的 token ,存储在浏览器的localStorage 里;
        3、前端每次路由跳转,判断 localStorage 有无 token ,没有则跳转到登录页,有则请求获取用户信息,改变登录状态;
        4、每次请求接口,在 Axios 请求头里携带 token;
        5、后端接口判断请求头有无 token,没有或者 token 过期,返回标记信息;前端得到标记信息,重定向到登录页面;

    1、前端发起登录请求

    login(){
    				let para={"username":this.name,"password":this.password};
                    this.$http.post("/user/login",para).then((res)=> {
                        let {success,msg,result}=res.data;
                        if(!success){//登录失败
                            this.errorMsg=msg;
                        }else {//登录成功
                            this.errorMsg="";//清空errormsg
                            //获取登录用户
                            let loginUser=result.loginUser;
                            let  token=result.token;
                            //保存到浏览器
                            localStorage.setItem("token",token)
                            localStorage.setItem("loginUser",JSON.stringify(loginUser))
                            location.href="index.html";
                            console.debug(loginUser,token);
                        }
                    });
                    },

            登录成功后,将后端返回的token和loginUser存储在localStorage中。

    2、后端接受并处理请求

            后端生成一个token字符串作为key值,将登录的user转为json字符串作为value值。存储在redis服务器中,有效期30分钟,然后将这两个值返回给前端。前端接收到,使用localStorage保存到浏览器。

    controller层:

       /**
         * 通过邮箱或者手机号登陆
         * @return
         */
        @PostMapping("/login")
        public AjaxResult login(@RequestBody User user){
            try {
                //通过用户名和密码查询并进行校验
                AjaxResult ajaresult = userService.login(user);
                //校验通过
                if(ajaresult.getSuccess()) {
                    //通过用户名查找user
                    User loginuser = userService.loadByUsername(user.getUsername());
                    //随机产生redis的key值 
                    String token = UUID.randomUUID().toString();
                    //将loginuser转化为json字符串 存入redis    有效期30分钟
                    RedisUtils.INSTANCE.set(token, JSONObject.toJSONString(loginuser),30*60);
                    Map<String,Object> map=new HashMap<>();
                    //把token存储起来
                    map.put("token",token);
                    //把对象存储起来 
                    map.put("loginUser",loginuser);
                    //把token和loginUser存储起来返回到前端
                    return new AjaxResult().setResult(map);
                }
                //为通过校验直接返回
                return ajaresult;
            } catch (Exception e) {
                e.printStackTrace();
                return new AjaxResult(false,e.getMessage());
            }
        }
    

    Service层:

        /**
         * 通过邮箱或者手机号的的登录
         * @param user
         * @return
         */
        @Override
        public AjaxResult login(User user){
            if (StringUtils.isBlank(user.getUsername())||StringUtils.isBlank(user.getPassword()))
                return new AjaxResult(false,"请输入用户名和密码!!");
            //通过用户名查询用户 查询到之后获取用户盐值
            User byusername = userMapper.loadByUsername(user.getUsername());
            if(byusername==null)
                return new AjaxResult(false,"用户名错误!!");
    
            //获取通过md5盐值加密的密码
            String s = MD5Utils.encrypByMd5(user.getPassword()+byusername.getSalt());
            user.setPassword(s);
            User loginu = userMapper.login(user);
            if(loginu==null)
                return new AjaxResult(false,"密码错误!!");
            if(loginu.getState()==UserConstant.DISABLED)//用户未激活
                return new AjaxResult(false,"用户未激活,请激活用户后登录!!");
            return  new AjaxResult();
        }
    

    3、配置前端前置拦截器

            接下来,前端每次发送请求都要携带这个token值,需要判断通过是否存在token判断用户是否登录之前使用cookie发起请求时是会自动携带所有cookie信息。

            在前端页面加一个前置拦截器,就是发送任何请求前先执行这个前端拦截器函数,这里面主要从localStorage里获取token值,然后将其添加到请求头信息里 .每次发起请求 请求头中都会包含U-TOKEN 这个信息。

            在common.js中配置:

    //前置拦截器
    //给axios请求添加一个前置拦截器   每次发起请求都先执行下面这个函数
    //1 使用axios前置拦截器,让所有的请求都携带uToken
    axios.interceptors.request.use(config=>{
        //携带token
        let uToken =  localStorage.getItem("token");
        if(uToken){//如果token有值  我就在请求头信息里添加一个教U-TOKEN的值
            config.headers['U-TOKEN']=uToken;
        }
        return config;
    },error => {
        Promise.reject(error);
    })
    

    4、后端配置拦截器

    自定义拦截器LoginInterceptor:

    package com.rk.pethome.user.interceptor;
    import com.rk.pethome.basic.util.RedisUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * 登录拦截器
     */
    @Component
    public class LoginInterceptor  implements HandlerInterceptor{
    
        //前置拦截
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
            //1 放行 /login /logout /dfs /user/phoneRegister
            String uri = request.getRequestURI();
            System.out.println(uri);
    
          /* if (CollectUtils.contain(uri, Arrays
                    .asList("/user/login","/logout","/dfs","/user/phoneRegister")))
                return true;*/
            // 2 拦截处理
            //2.1 判断请求头里面是否携带U-TOKEN A-TOKEN,如果没有携带返回没有用户的错误
            String uToken = request.getHeader("U-TOKEN");
            //用户没有登录
            if(StringUtils.isBlank(uToken)){
                writeNoUserError(response,"noUser");
                return false;
            }
            //2.2 判断redis中是否可以获取U-TOKEN A-TOKEN,如果获取不到没有用户的错误
            String user = RedisUtils.INSTANCE.get(uToken);
            //登录的用户已经过期了
            if( StringUtils.isBlank(user)){
                writeNoUserError(response,"expireUser");
                return false;
            }
            //如果没有过期,刷新一下过期时间
            //2.2.1 刷新session过期-redis里面的一个值
            RedisUtils.INSTANCE.set(uToken,user,30*60);
            return true;
        }
    
        /**
         *  {"success":false,"message":"noUser"}
         * response.getWriter().write("{'success':false,messge:noUser}");
         * @param response
         */
        private void writeNoUserError(HttpServletResponse response,String message) {
            PrintWriter writer = null;
            try {
                response.setCharacterEncoding("utf-8"); //返回编码格式
                response.setContentType("application/json; charset=utf-8"); // json方式放回
                writer = response.getWriter();
                writer.write("{\"success\":false,\"message\":"+"\""+message+"\""+"}");
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if (writer != null) {
                    writer.close();
                }
            }
        }
    }
    

    添加配置ItsourceWebMvcConfigurer:

    /user/*路径下的请求放行

    @Configuration
    public class ItsourceWebMvcConfigurer implements WebMvcConfigurer {
    
        @Autowired
        private LoginInterceptor loginInterceptor;
    
        /*
                /* :拦截所有的请求,请求只有一级
                /** :拦截所有的请求    可以拦截多级请求
    
         */
        //添加拦截器
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(loginInterceptor).addPathPatterns("/**")
                    .excludePathPatterns("/user/*");
        }
    }

            此时请求到了(/user/login请求不会被拦截)后端拦截器,拦截器首先从请求头获取token值,如果为空,说明未登录,拦截请求并且返回{success:false,message:noUser}。
    如果获取到token值,说明用户登录过,因为存储在前端localStorage的值是短时间内不会过期,除非手动清空缓存。但是保存在redis的该用户只在30分钟内有效。也就是常见的登录到一个页面长时间过后需要再次登录。此时根据前端传过来的token去redis服务器中获取值,如果没有获取到值,说明登录已过期。就拦截请求并且返回{success:false,message:expireUser}。如果获取到了值,就将token和获取到的value值(上次登录的用户)再次存储到redis服务器,刷新过期时间。

     5、配置前端后置拦截器

            在conmmon.js中:

    //后置拦截器
    //2 使用axios后置拦截器,处理没有登录请求
    axios.interceptors.response.use(result=>{
        let data = result.data;
        console.debug(data)
        console.log(data.success);
        console.log(data.message);
        //用户没有登录
        if(!data.success && data.message==="noUser") {
            alert("用户未登录!");
            location.href = "/login.html";
        }
    
        //后台redis用户过期了  清空localStorage里面的值
        if(!data.success&&data.message==="expireUser"){
            alert("登录已过期!");
            localStorage.removeItem("token");
            localStorage.removeItem("loginUser");
            location.href = "/login.html";
            return;
    
        }
        return result;
    },error => {
        Promise.reject(error);
    })

            请求返回后,前端界面再配置一个后置拦截器,所有返回值都会先经过这个后置拦截器。拦截器判断success和message   如果是false和noUser就说明用户未登录,就跳转到登录界面。接下来再判断message的值是否是expireUser,如果是则  登录的用户已过期,就清空localStorage里面存储的数据,然后跳转到登录界面。如果用户已登录并且未过期,则将后端返回的result返回给前端调用接口的函数。 

    展开全文
  • SpringBoot+vue前后端分离【实现拦截器】1、注意(登录后jsessionid不一致的问题:解决思路)2、用户登录成功后在后台生成token3、前台存储token(使用localStorage)3.1 localStorage和sessionStorage的区别3.2 ...
  • 前后端分离实现登录拦截

    千次阅读 2020-09-20 16:10:30
    .allowedHeaders("*") } 3.2前端拦截 在main.js加上 axios.defaults.withCredentials = true 为了防止伪造参数,绕过前端路由限制,在每个页面钱都向后端发送一个请求,目的是经由拦截器验证服务端的登录状态,防止...
  • jeesite 前后端分离 跨域 拦截器

    千次阅读 2019-03-21 09:54:41
    最近用jeesite作为后端与前端进行交互,出现了跨域的...1、通过web.xml增加跨域拦截器cors <!-- 前后端分类跨域解决 --> <filter> <filter-name>cors</filter-name> <filter-class...
  • 注解类(Authorized.java) @Documented @Inherited @Target({ElementType.TYPE, ElementType.METHOD}) ...拦截器处理类(AuthInterceptor.java) public class AuthInterceptor extends HandlerInterceptorAdapte.
  • 前后端分离SpringBoot项目拦截器配置

    千次阅读 2020-03-26 16:01:30
    近期需要“搭建”基于SpringBoot框架的前后端分离项目,其中需要配置拦截器,多方查资料最终总结以下内容 拦截器配置 以下为拦截器的基本配置; 拦截的请求首先校验token信息,其中token信息放于redis中; 拦截器...
  • 前后端分离,后端springboot拦截器拦截非权限API不适用 前端vue使用vuex能使用vue的router的自我守卫和拦截。
  • 问题倒不难,但是这个问题之前被...但是在前后端分离中,页面的跳转统统交给前端去做,后端只提供数据,这种时候,权限管理不能再按照之前的思路来。 首先要明确一点,前端是展示给用户看的,所有的菜单显示或者隐藏
  • 前端在nginx里 ,想要获取前端的跳转路径(是一个大页面里放一个小页面 拦截小页面),实现指定页面拦截。获取的请求路径全是后端接口的路径,没法拦截成功。有解决办法吗
  • 感受一下前后端分离

    2018-08-26 18:14:47
    博客:https://blog.csdn.net/m0_37499059/article/details/82082825 1.springboot 2.跨域访问(CORS)和token校验 3.jjwt生成token 4.拦截器的使用 5.全局异常处理 6.postman工具使用
  • vue前后端分离 用户注册 登录拦截

    千次阅读 2020-06-28 21:53:12
    也是第一次做vue前后端分离 用户注册 登录拦截。项目已经搭建完成,下面分享一下搭建过程。 前端vue 1.login界面 2.注册页面 3.展示页面 前台页面在element官网组件有详细教程,这里不再赘诉。主要还是后台逻辑的...
  • 搭建spring-boot+vue前后端分离框架并实现登录功能

    万次阅读 多人点赞 2019-05-21 17:28:00
    一、环境、工具 jdk1.8 ... #监控统计拦截的filters ...配置pom.xml文件中generator插件所需要的配置文件${basedir}/src/main/resources/...至此前后端都已经搭建完毕,启动项目即可访问  源码下载:
  • 使用WebMvcConfigurer接口配置拦截器, 跨域请求会失效, 好像是拦截放行后不会带有跨域信息, 导致失败。下面两个配置可以同时生效。 跨域配置: @Configuration @EnableWebMvc public class CorsConfig implements ...
  • Auth-demo 基于Springboot前后端分离式,利用拦截器完成登录拦截的demo 详细介绍请戳
  • 主要介绍了springboot+angular4前后端分离 跨域问题解决详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • //如果U-TOKEN为空则证明没有登录 try { //没有登录 就告诉前台 应该跳到登录页面 (前台用后置拦截器接受:在每次请求后响应之前拦截,就是有res以后执行成功函数之前) //告诉前台我要传的数据格式 和字符集 ...
  • SpringSecurity+自定义登录界面+JwtToken+拦截器+前后端分离
  • 拦截实体类 import com.kol.constant.SessioNameCons; import com.kol.domain.UserDomain; import com.kol.msg.Msg; import com.kol.utils.JsonUtils; import org.springframework.context.annotation.Bean; import ...
  • Springboot + Spring Security 实现前后端分离登录认证及权限控制前言本文主要的功能文章目录一、数据库表设计建表语句初始化表数据语句二、Spring Security核心配置:WebSecurityConfig三、用户登录认证逻辑:...
  • 前后端分离登录

    千次阅读 2021-11-16 18:44:28
    而我们的网站都是设计成多个页面的,所在页面跳转过程中我们需要知道用户的状态,尤其是用户登录的状态,这样我们在页面跳转后我们才知道是否可以让用户有权限来操作一些功能或是查看一些数据。 所以,我们每个页面...
  • 前后端分离实现,后端基于SpringBoot开发,前端使用Jquery实现Ajax请求,测试中请求可以正常到达,但是在拦截器或者过滤器中总获取不到自定义请求头。解决方法如下,直接上代码。 网上文章总感觉缺点意思。。 前端...
  • springboot+shiro前后端分离异常拦截,进行操作。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,303
精华内容 5,721
关键字:

前后端分离登录拦截器