springboot+vue实现单点登录

目录

1. 环境

2. 实现步骤

2.1 集成Redis

2.2 实现Vue登录页面

2.3 实现Spring Boot后端

2.3.1 用户认证

2.3.2 生成令牌

2.3.3 验证令牌

2.4 实现单点登录

2.4.1 登录流程


单点登录(Single Sign-On,SSO)是指在多个应用系统中,用户只需登录一次就可以访问多个应用系统的能力。下面将介绍如何使用Vue和Spring Boot实现单点登录。

1. 环境
  • JDK 1.8+
  • Spring Boot 2.x
  • Vue.js 2.x
  • Redis
2. 实现步骤
2.1 集成Redis

首先需要安装Redis以及Java的Redis客户端,这里使用Jedis作为Redis客户端。在Spring Boot项目中集成Redis,需要添加以下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.3.0</version>
</dependency>

然后在application.properties中配置Redis连接信息:

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
2.2 实现Vue登录页面

在Vue项目中,可以使用Axios发送登录请求,获取服务端返回的令牌,并将令牌保存到Cookie中:

// 登录​​​​​​​
login() {
    let params = new URLSearchParams();
    params.append('username', this.username);
    params.append('password', this.password);
    axios.post('/api/login', params).then((response) => {
        console.log(response);
        if (response.data.code === 200) {
            // 保存令牌
            let token = response.data.token;
            Cookie.set('token', token, { expires: 1 });
            // 跳转到首页
            this.$router.push('/home');
        } else {
        	// 登录失败
            this.$message.error(response.data.msg);
        }
    }).catch((error) => {
        console.log(error);
    });
}
2.3 实现Spring Boot后端

服务端使用Spring Boot实现单点登录,需要实现以下几个步骤:

2.3.1 用户认证

用户登录后,服务端需要进行用户认证。这里使用Spring Security进行用户认证,需要添加以下依赖:

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

然后在WebSecurityConfigurerAdapter中进行配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
                .antMatchers("/api/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/api/login").permitAll()
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        PrintWriter out = response.getWriter();
                        out.write("{\"code\":200,\"msg\":\"登录成功\"}");
                        out.flush();
                        out.close();
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        PrintWriter out = response.getWriter();
                        out.write("{\"code\":401,\"msg\":\"" + exception.getMessage() + "\"}");
                        out.flush();
                        out.close();
                    }
                })
                .and()
                .logout()
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        PrintWriter out = response.getWriter();
                        out.write("{\"code\":200,\"msg\":\"退出登录成功\"}");
                        out.flush();
                        out.close();
                    }
                })
                .deleteCookies("JSESSIONID", "SESSION");
    }

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

其中,UserDetailsServiceImpl实现UserDetailsService接口,从数据库中获取用户信息:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.getUserByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }

        List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(user.getRoles().split(","));

        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
    }
}

PasswordEncoder使用BCryptPasswordEncoder,对用户密码进行加密:

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
2.3.2 生成令牌

用户登录成功后,服务端需要生成令牌,并将令牌存储到Redis中。这里使用JWT作为令牌生成工具,需要添加以下依赖:

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

然后在JwtUtil中实现令牌生成方法:

public String generateToken(String username) {
    Date now = new Date();
    Date expiration = new Date(now.getTime() + expirationTime * 1000);

    return Jwts.builder()
            .setClaims(new HashMap<>())
            .setSubject(username)
            .setIssuedAt(now)
            .setExpiration(expiration)
            .signWith(SignatureAlgorithm.HS512, secret)
            .compact();
}

2.3.3 验证令牌

用户访问其他应用系统时,需要验证用户是否已登录,这里使用Filter实现令牌验证。定义一个AuthFilter,实现doFilter方法:

public class AuthFilter extends OncePerRequestFilter {

    private static final String AUTH_HEADER = "Authorization";

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authHeader = request.getHeader(AUTH_HEADER);
        if (StringUtils.isNotBlank(authHeader) && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            Claims claims = null;
            try {
                claims = jwtUtil.parseToken(token);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (claims != null && claims.getExpiration().after(new Date())) {
                String username = claims.getSubject();
                // 验证用户是否已登录
                if (StringUtils.isNotBlank(username) && JedisUtil.exists(username)) {
                    filterChain.doFilter(request, response);
                    return;
                }
            }
        }
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write("{\"code\":401,\"msg\":\"请先登录\"}");
        out.flush();
        out.close();
    }}

然后在WebSecurityConfigurerAdapter中添加AuthFilter:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthFilter authFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
                .antMatchers("/api/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class)
    	// ...
    }
}
2.4 实现单点登录

实现了用户认证、生成令牌和验证令牌之后,就可以实现单点登录了。

2.4.1 登录流程

用户在一个应用系统中登录,然后访问其他应用系统时,其他应用系统需要验证用户是否已登录。这里的步骤如下:

  1. 用户在第一个应用系统中登录,该系统使用单点登录技术生成一个令牌,并将该令牌返回给用户。
  2. 用户在访问其他应用系统时,在 HTTP 请求中添加该令牌。
  3. 接收请求的应用系统将该令牌发送到单点登录服务器进行验证。
  4. 单点登录服务器验证该令牌,如果验证通过,则说明用户已登录,应用系统可以允许用户访问该系统的资源。
  5. 如果验证未通过,则说明用户未登录或者登录已过期,应用系统需要引导用户重新登录或者跳转到登录页面。
  6. 用户在其他应用系统中的操作会触发新的 HTTP 请求,其中仍然需要携带该令牌进行身份验证。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring BootVue 前后分离项目中集成 CAS (Central Authentication Service,集中认证服务) 单点登录通常是一个常见的需求,这样可以提供统一的登录入口,用户在登录一次后可以在多个系统间无需再次登录。下面是集成的基本步骤: 1. 添加依赖:首先,你需要在 Spring Boot 项目的 pom.xml 或者 build.gradle 文件中添加 CAS 的客户端库依赖。 2. 配置 CAS:在 Spring Boot 应用中,设置 CAS 客户端配置。这包括 CAS 服务器的地址、应用的服务名、回调 URL(处理 CAS 登录成功后的重定向)等信息。通常会在 `application.properties` 或 `application.yml` 中配置 cas相关的属性。 ```properties cas.server-url=http://cas.example.com/cas cas.login-url=http://cas.example.com/cas/login cas.logout-url=http://cas.example.com/cas/logout cas.client-id=your-client-id cas.redirect-uri=http://localhost:8080/login-callback ``` 3. 创建 CAS 授权过滤器:在 Spring Security 配置中添加一个 CASFilter,这个过滤器会检查用户的 CAS 认证状态,并在未授权时引导用户到 CAS 登录页面。 4. 实现登录回调:当用户通过 CAS 登录成功后,CAS 会将用户信息发送回你指定的回调 URL。在这里,你需要捕获这些信息并进行处理,比如设置 session 或者 JWT 令牌,然后重定向回前端。 5. Vue.js 部分:在前端 Vue 项目中,使用 Axios 或其他 HTTP 请求库向后端发起请求时,带上一个 token 或者 JWT 以证明用户身份。如果请求头没有验证信息,后端可以根据配置判断是否需要转发到 CAS 进行二次认证。 6. 登出处理:在前端和后端都实现注销操作,前端可以调用后端提供的 CAS 注销接口,后端再调用 CAS 注销服务并清空 session 或 JWT。 相关问题: 1. 如何在 Spring Boot 中启用 CAS 的客户端支持? 2. 在 Vue 中如何处理 CAS 登录成功后的回调? 3. 如何确保前端和后端的注销操作能够同步 CAS 服务?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只java小菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值