SpringBoot + Redis实现单点登录

1.为什么有单点登录

web应用早已从久远的单系统发展到如今由多系统组成的应群,面对如此众多的系统,用户难道要一个一个登录,这显然不现实。单点登录就是为了解决这一问题。

web应用的复杂性应该由系统内部承担,而不是用户。无论web系统内部多么复杂,对用户而言,都是一个统一的整体,也就是说,用户访问web系统的整个应用群与访问单个系统一样,登录一次就够了。

2.单点登录的实现方案

1.session共享实现单点登录,这种方案并不常用,因为借助于session,而session的底层是cookie,所以,如果使用这种方式,要求客户端必须支持cookie

2.使用token,可以使用原本的OAuth2协议,也可以自定义认证流程

具体流程

(1)客户端发送用户名/密码到服务器
(2)服务器拿到用户名密码后进行认证操作
    (2.1) 认证成功的话,生成一个token,以token为key,用户信息的JSON字符串为value,将数据存储到redis上,同时,设置数据过期时间,最后再将token返回    给客户端。
    (2.2)认证失败的话,直接返回认证失败的信息即可。
(3)客户端拿到服务端返回的token后,将之保存,以后,客户端发起任何请求时,都需要携带上该参数,服务端在处理客户端请求时,提取出token,去redis中查询数据,如果能查到,就能获取到用户的所有信息,包括用户角色、权限等。如果获取不到,说明用户未登录或者登录已过期,提示用户重新登录。
(4)服务端还需要额外提供一个刷新token的接口,用户可以拿着旧的token来换取一个新的token。

3.代码实现

1.创建一个SpringBoot项目并整合Redis,SpringBoot整合Redis的具体实现可参考SpringBoot整合Redis。

pom.xml添加如下依赖


            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            org.apache.commons
            commons-pool2
        
        
            org.springframework.boot
            spring-boot-starter-web
        

2在resources/application.properties配置文件中配置连接Redis的信息 

spring.redis.database=0
spring.redis.host=192.168.2.10
spring.redis.port=6379
spring.redis.password=123456
spring.redis.lettuce.pool.min-idle=5
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=1ms
spring.redis.lettuce.shutdown-timeout=100ms

 3.创建LoginController前端控制器

@RestController
public class LoginController {
    @Autowired
    RedisTemplate redisTemplate;

    /**
     * 登录接口
     * @param user User对象
     * @return s 存入Redis里的凭证的key
     * @throws JsonProcessingException
     */
    @PostMapping(value = "/doLogin",produces = "text/html;charset=utf-8")
    public String login(User user) throws JsonProcessingException {
        ValueOperations ops = redisTemplate.opsForValue();
        if ("zhangsan".equals(user.getUsername()) && "123456".equals(user.getPassword())){
            String s = UUID.randomUUID().toString();
            ObjectMapper om = new ObjectMapper();
            ops.set(s,om.writeValueAsString(user));//将凭证存入Redis
            redisTemplate.expire(s,30,TimeUnit.MINUTES);//设置过期时间,30分钟
            return s;
        }
        return "登录失败!";
    }
    /**
     * 测试接口
     * @param token
     * @return
     */
    @GetMapping("/hello")
    public String hello(String token){
        return "hello";
    }
}

4.创建一个LoginInterceptor拦截器,拦截前端请求,判断是否合法。

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    RedisTemplate redisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getParameter("token");
        ValueOperations ops = redisTemplate.opsForValue();
        String user = (String) ops.get(token);
        if (user==null || "".equals(user)){
            response.setContentType("text/html;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.write("请求非法");
            return false;
        }
        return true;
    }
}

5.创建WebMVCConfig 配置类,定义拦截器拦截规则

@Configuration
public class WebMVCConfig implements WebMvcConfigurer {
    @Autowired
    LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")//需要拦截的路径
                .excludePathPatterns("/doLogin");//排除/doLogin路径
    }
}

4.测试

1.访问http://localhost:8080/doLogin

SpringBoot + Redis实现单点登录_第1张图片

2.访问http://localhost:8080/hello?token=40a629cf-6d6e-4c02-bb59-2114a807d355。token访问doLogin返回的值 

SpringBoot + Redis实现单点登录_第2张图片

3.访问http://localhost:8080/hello?token=40a629cf-6d6e-4c02-bb59-2114a807d355。如果token过期或没有登录则提示请求非法

SpringBoot + Redis实现单点登录_第3张图片

 

 

 

 

 

你可能感兴趣的:(SpringBoot)