web应用早已从久远的单系统发展到如今由多系统组成的应群,面对如此众多的系统,用户难道要一个一个登录,这显然不现实。单点登录就是为了解决这一问题。
web应用的复杂性应该由系统内部承担,而不是用户。无论web系统内部多么复杂,对用户而言,都是一个统一的整体,也就是说,用户访问web系统的整个应用群与访问单个系统一样,登录一次就够了。
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。
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路径
}
}
1.访问http://localhost:8080/doLogin
2.访问http://localhost:8080/hello?token=40a629cf-6d6e-4c02-bb59-2114a807d355。token访问doLogin返回的值
3.访问http://localhost:8080/hello?token=40a629cf-6d6e-4c02-bb59-2114a807d355。如果token过期或没有登录则提示请求非法