SSM商城项目(十一)
1. 学习计划
1、sso注册功能实现
2、sso登录功能实现
3、通过token获得用户信息
Ajax跨域请求(jsonp)
2. Sso系统工程搭建
需要创建一个sso服务工程,可以参考e3-manager创建。
e3-sso(pom聚合工程)
|--e3-sso-interface(jar)
|--e3-sso-Service(war)
e3-sso-web
3. 服务接口实现
3.1. 检查数据是否可用
3.1.1. 功能分析
请求的url:/user/check/{param}/{type}
参数:从url中取参数
1、String param(要校验的数据)
2、Integer type(校验的数据类型)
响应的数据:json数据。e3Result,封装的数据校验的结果true:成功false:失败。
业务逻辑:
1、从tb_user表中查询数据
2、查询条件根据参数动态生成。
3、判断查询结果,如果查询到数据返回false。
4、如果没有返回true。
5、使用e3Result包装,并返回。
3.1.2. Dao层
从tb_user表查询。可以使用逆向工程。
3.1.3. Service
参数:
1、要校验的数据:String param
2、数据类型:int type(1、2、3分别代表username、phone、email)
返回值:e3Result
@Service public class RegisterServiceImpl implements RegisterService{ @Autowired private TbUserMapper userMapper; @Override public E3Result checkData(String param, int type) { // 1、从tb_user表中查询数据 TbUserExample example = new TbUserExample(); Criteria criteria = example.createCriteria(); // 2、查询条件根据参数动态生成。 //1、2、3分别代表username、phone、email if (type == 1) { criteria.andUsernameEqualTo(param); } else if (type == 2) { criteria.andPhoneEqualTo(param); } else if (type == 3) { criteria.andEmailEqualTo(param); } else { return E3Result.build(400, "非法的参数"); } //执行查询 List<TbUser> list = userMapper.selectByExample(example); // 3、判断查询结果,如果查询到数据返回false。 if (list == null || list.size() == 0) { // 4、如果没有返回true。 return E3Result.ok(true); } // 5、使用E3Result包装,并返回。 return E3Result.ok(false); } }
发布服务
<dubbo:service interface="cn.e3mall.sso.service.RegisterService" ref="registerServiceImpl" timeout="600000"/>
3.1.4. 表现层
需要在e3-sso-web中实现。
Controller
请求的url:/user/check/{param}/{type}
参数:从url中取参数1、String param(要校验的数据)2、Integer type(校验的数据类型)
响应的数据:json数据。e3Result,封装的数据校验的结果true:成功false:失败。
@Controller public class RegitsterController { @Autowired private RegisterService registerService; @RequestMapping("/user/check/{param}/{type}") @ResponseBody public E3Result checkData(@PathVariable String param,@PathVariable Integer type){ E3Result e3Result=registerService.checkData(param, type); return e3Result; } }
引用服务
<dubbo:reference interface="cn.e3mall.sso.service.RegisterService" id="registerService" />
3.2. 用户注册
3.2.1. 功能分析
请求的url:/user/register
参数:表单的数据:username、password、phone
返回值:json数据。e3Result
接收参数:使用TbUser对象接收。
请求的方法:post
业务逻辑:
1、使用TbUser接收提交的请求。
2、补全TbUser其他属性。
3、密码要进行MD5加密。
4、把用户信息插入到数据库中。
5、返回e3Result。
3.2.1. Dao层
可以使用逆向工程。
3.2.2. Service层
参数:TbUser
返回值:E3Result
@Override public E3Result createUser(TbUser user) { // 1、使用TbUser接收提交的请求。 if (StringUtils.isBlank(user.getUsername())) { return E3Result.build(400, "用户名不能为空"); } if (StringUtils.isBlank(user.getPassword())) { return E3Result.build(400, "密码不能为空"); } //校验数据是否可用 E3Result result = checkData(user.getUsername(), 1); if (!(boolean) result.getData()) { return E3Result.build(400, "此用户名已经被使用"); } //校验电话是否可以 if (StringUtils.isNotBlank(user.getPhone())) { result = checkData(user.getPhone(), 2); if (!(boolean) result.getData()) { return E3Result.build(400, "此手机号已经被使用"); } }// 2、补全TbUser其他属性。 user.setCreated(new Date()); user.setUpdated(new Date()); // 3、密码要进行MD5加密。 String md5Pass = DigestUtils.md5DigestAsHex(user.getPassword().getBytes()); user.setPassword(md5Pass); // 4、把用户信息插入到数据库中。 userMapper.insert(user); // 5、返回E3Result。 return E3Result.ok(); }
发布服务
3.2.4. 表现层
引用服务。
Controller:
请求的url:/user/register
参数:表单的数据:username、password、phone、email
返回值:json数据。e3Result
接收参数:使用TbUser对象接收。
请求的方法:post
@RequestMapping("/user/register") public E3Result createUser(TbUser tbuser){ E3Result e3=registerService.createUser(tbuser); return e3; }
33. 用户登录
3.3.1. 功能分析
请求的url:/user/login
请求的方法:POST
参数:username、password,表单提交的数据。可以使用方法的形参接收。
返回值:json数据,使用e3Result包含一个token。
业务逻辑:
登录的业务流程:
登录的处理流程:
1、登录页面提交用户名密码。
2、登录成功后生成token。Token相当于原来的jsessionid,字符串,可以使用uuid。
3、把用户信息保存到redis。Key就是token,value就是TbUser对象转换成json。
4、使用String类型保存Session信息。可以使用“前缀:token”为key
5、设置key的过期时间。模拟Session的过期时间。一般半个小时。
6、把token写入cookie中。
7、Cookie需要跨域。例如www.e3.com\sso.e3.com\order.e3.com,可以使用工具类。
8、Cookie的有效期。关闭浏览器失效。
9、登录成功。
3.3.2. Dao层
查询tb_user表。单表查询。可以使用逆向工程。
3.3.3. Service层
参数:
1、用户名:String username
2、密码:String password
返回值:e3Result,包装token。
业务逻辑:
1、判断用户名密码是否正确。
2、登录成功后生成token。Token相当于原来的jsessionid,字符串,可以使用uuid。
3、把用户信息保存到redis。Key就是token,value就是TbUser对象转换成json。
4、使用String类型保存Session信息。可以使用“前缀:token”为key
5、设置key的过期时间。模拟Session的过期时间。一般半个小时。
6、返回e3Result包装token。
@Service public class UserServiceImpl implements UserService{ @Autowired private TbUserMapper userMapper; @Autowired private JedisClient jedisClient; @Value("${SESSION_EXPIRE}") private Integer SESSION_EXPIRE; @Override public E3Result login(String username, String password) { // 1、判断用户名密码是否正确。 TbUserExample example = new TbUserExample(); Criteria criteria = example.createCriteria(); criteria.andUsernameEqualTo(username); //查询用户信息 List<TbUser> list = userMapper.selectByExample(example); if (list == null || list.size() == 0) { return E3Result.build(400, "用户名或密码错误"); } TbUser user = list.get(0); //校验密码 if (!user.getPassword().equals(DigestUtils.md5DigestAsHex(password.getBytes()))) { return E3Result.build(400, "用户名或密码错误"); } // 2、登录成功后生成token。Token相当于原来的jsessionid,字符串,可以使用uuid。 String token = UUID.randomUUID().toString(); // 3、把用户信息保存到redis。Key就是token,value就是TbUser对象转换成json。 // 4、使用String类型保存Session信息。可以使用“前缀:token”为key user.setPassword(null); jedisClient.set("SESSION:" +token, JsonUtils.objectToJson(user)); // 5、设置key的过期时间。模拟Session的过期时间。一般半个小时。 jedisClient.expire("SESSION:"+ token, SESSION_EXPIRE); // 6、返回e3Result包装token。 return E3Result.ok(token); } }
发布服务
3.3.4. 表现层
引用服务
Controller
请求的url:/user/login
请求的方法:POST
参数:username、password,表单提交的数据。可以使用方法的形参接收。
HttpServletRequest、HttpServletResponse
返回值:json数据,使用e3Result包含一个token。
业务逻辑:
1、接收两个参数。
2、调用Service进行登录。
3、从返回结果中取token,写入cookie。Cookie要跨域。
4、响应数据。Json数据。e3Result,其中包含Token。
@Autowired private UserService userService; @Value("${TOKEN_KEY}") private String TOKEN_KEY; @RequestMapping("/user/login") @ResponseBody public E3Result login(String username,String password,HttpServletRequest request,HttpServletResponse response){ E3Result e3Result = userService.login(username, password); if(e3Result.getStatus()==200){ String token=e3Result.getData().toString(); CookieUtils.setCookie(request, response, TOKEN_KEY, token); } return e3Result; }
3.4. 通过token查询用户信息
3.4.1. 功能分析
请求的url:/user/token/{token}
参数:String token需要从url中取。
返回值:json数据。使用e3Result包装Tbuser对象。
业务逻辑:
1、从url中取参数。
2、根据token查询redis。
3、如果查询不到数据。返回用户已经过期。
4、如果查询到数据,说明用户已经登录。
5、需要重置key的过期时间。
6、把json数据转换成TbUser对象,然后使用e3Result包装并返回。
3.4.2. Dao层
使用JedisClient对象。
3.4.3. Service层
参数:String token
返回值:e3Result
@Service public class TokenServiceImpl implements TokenService { @Autowired private JedisClient jedisClient; @Value("${SESSION_EXPIRE}") private Integer SESSION_EXPIRE; @Override public E3Result getUserByToken(String token) { //根据token到redis中取用户信息 String json = jedisClient.get("SESSION:" + token); //取不到用户信息,登录已经过期,返回登录过期 if (StringUtils.isBlank(json)) { return E3Result.build(201, "用户登录已经过期"); } //取到用户信息更新token的过期时间 jedisClient.expire("SESSION:" + token, SESSION_EXPIRE); //返回结果,E3Result其中包含TbUser对象 TbUser user = JsonUtils.jsonToPojo(json, TbUser.class); return E3Result.ok(user); } }
3.4.3. 表现层
请求的url:/user/token/{token}
参数:String token需要从url中取。
返回值:json数据。使用e3Result包装Tbuser对象。
@Controller public class TokenController { @Autowired private TokenService tokenService; @RequestMapping("/user/token/{token}") @ResponseBody public E3Result getUserByToken(@PathVariable String token) { E3Result result = tokenService.getUserByToken(token); return result; } }
5. 登录注册页面整合首页
5.1. 首页跳转到登录、注册页面
5.2. 首页展示用户名
1、当用户登录成功后,在cookie中有token信息。
2、从cookie中取token根据token查询用户信息。
3、把用户名展示到首页。
方案一:在Controller中取cookie中的token数据,调用sso服务查询用户信息。
方案二:当页面加载完成后使用js取token的数据,使用ajax请求查询用户信息。
问题:服务接口在sso系统中。Sso.e3.com(localhost:8089),在首页显示用户名称,首页的域名是www.e3.com(localhost:8083),使用ajax请求跨域了。
Js不可以跨域请求数据。
什么是跨域:
1、域名不同
2、域名相同端口不同。
解决js的跨域问题可以使用jsonp。
Jsonp不是新技术,跨域的解决方案。使用js的特性绕过跨域请求。Js可以跨域加载js文件。
5.3. Jsonp原理
5.4. Json实现
5.4.1. 客户端
使用jQuery。
5.4.2. 服务端
1、接收callback参数,取回调的js的方法名。
2、业务逻辑处理。
3、响应结果,拼接一个js语句。