Dowemo
0 0 0 0

introduction
Sso english full single sign on, single single sign on. Sso is in multiple application systems, and users can access all the trusted application systems once only once. It includes the mechanism to map this primary login to other applications for the same user 's login mechanism. It's one of the most popular enterprise business integration solutions.

Why is the.

When you write a case, if there's only one web project. If you want to access information, we usually write an interceptor, and you can't get the user information from session. If you don't need to go back to the login page, the user is logged in to the session, then the user center has no problem. And then it isn't a problem in a web project.
这里写图片描述

If the system is distributed, for example, it's a distributed, what member module, commodity management module, shopping cart module, order module, etc. If you use one of the above verification methods, the access to a module finds that there's no login, then the jump page is logged in, the information is stored in. Furthermore, clustering is also done for a high degree of concurrency, even if it's two access to the same module, and it's likely to have access to another server in the cluster so that there's a problem with multiple requests for login.
这里写图片描述

Solution: we can use the entire session server ( sso system ) to deal with login problems, so that users will be logged on every visit to the user center. If the user is logged in, there's no login to jump to the login page, we need redis to simulate the previous session, and we'll store the user information into redis, where we store the user information in redis.

How do you store, use what key to do value. In a web project, user information stored in session is the session. Setarrtribute ("admin", user ), which is the session, getattribute ("admin").

It's natural to think of user id as key, but don't do this because it isn't possible to distinguish between different connections, such as you logged on to tom in a computer. Then change the computer b, but don't log in directly but directly access the user center ( such as order settlement ). It can be accessed because it stores such a key pair in redis, and then it can be taken out of the user info description, so it's important to access it. But this is unreasonable, and you shouldn't be able to log on to the user 's center.
When using a web project with a tomcat server, tomcat is a different way to distinguish between a different connection, in fact tomcat generates a jsessionid as an identity, and then returns to the browser to save the jsessionid to a cookie, and then the gets the user information so that it doesn't have this jsessionid.
Now that you're planning to use redis to simulate session, you can do this. Every time a user logs in, a unique token is generated, using it as key, user information as value, and then returns the token to the browser. Next time
When accessing the user center, fetch the token from the cookie and use token from redis to determine whether the user 's center is allowed.
In this way, as long as the user is logged in, there's no user information in the cookie, so it's necessary to log in and analyze it and analyze it.
这里写图片描述

jar package
In e3-common ( jar ), there are some tool classes.
In e3-manager-pojo ( jar ), the mapper and mapping files are stored in the mybatis.
1, sso engineering building
E3-sso ( pom )
| -e3-sso-interface ( jar )
| -e3-sso-service ( war )
E3-sso-web ( war )

E3 sso requires the jar package introduced
Pom. Xml.

<groupId>cn.e3mall</groupId><artifactId>e3-sso</artifactId><version>0.0.1-SNAPSHOT</version><packaging>pom</packaging><modules><module>e3-sso-interface</module><module>e3-sso-service</module></modules><dependencies><dependency><groupId>cn.e3mall</groupId><artifactId>e3-common</artifactId><version>0.0.1-SNAPSHOT</version></dependency></dependencies><!-- 配置tomcat插件 --><build><plugins><plugin><!-- 配置Tomcat插件 --><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><configuration><path>/</path><!-- 表示访问时候不带工程名的 --><port>8087</port></configuration></plugin></plugins></build>

E3 sso interface introduction jar package
Pom. Xml.

<parent><groupId>cn.e3mall</groupId><artifactId>e3-sso</artifactId><version>0.0.1-SNAPSHOT</version></parent><artifactId>e3-sso-interface</artifactId><dependencies><dependency><groupId>cn.e3mall</groupId><artifactId>e3-manager-pojo</artifactId><version>0.0.1-SNAPSHOT</version></dependency></dependencies>

E3 sso service introduction jar package
Pom. Xml.

<parent><groupId>cn.e3mall</groupId><artifactId>e3-sso</artifactId><version>0.0.1-SNAPSHOT</version></parent><artifactId>e3-sso-service</artifactId><packaging>war</packaging><dependencies><dependency><groupId>cn.e3mall</groupId><artifactId>e3-manager-dao</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>cn.e3mall</groupId><artifactId>e3-sso-interface</artifactId><version>0.0.1-SNAPSHOT</version></dependency><!-- Spring --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jms</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId></dependency><!-- dubbo相关 --><dependency><groupId>com.alibaba</groupId><artifactId>dubbo</artifactId><exclusions><exclusion><groupId>org.springframework</groupId><artifactId>spring</artifactId></exclusion><exclusion><groupId>org.jboss.netty</groupId><artifactId>netty</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId></dependency><dependency><groupId>com.github.sgroschupf</groupId><artifactId>zkclient</artifactId></dependency></dependencies>

Spring profile 1: configuring data sources and integrating spring and mybatis

<!-- 数据库连接池 --><!-- 加载配置文件 --><context:property-placeholderlocation="classpath:conf/*.properties"/><!-- 数据库连接池 --><beanid="dataSource"class="com.alibaba.druid.pool.DruidDataSource"destroy-method="close"><propertyname="url"value="${jdbc.url}"/><propertyname="username"value="${jdbc.username}"/><propertyname="password"value="${jdbc.password}"/><propertyname="driverClassName"value="${jdbc.driver}"/><propertyname="maxActive"value="10"/><propertyname="minIdle"value="5"/></bean><!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 --><beanid="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 数据库连接池 --><propertyname="dataSource"ref="dataSource"/><!-- 加载mybatis的全局配置文件 --><propertyname="configLocation"value="classpath:mybatis/SqlMapConfig.xml"/></bean><beanclass="org.mybatis.spring.mapper.MapperScannerConfigurer"><propertyname="basePackage"value="cn.e3mall.mapper"/></bean>

Spring profile 2: configuring tra actio

<!-- 事务管理器 --><beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 数据源 --><propertyname="dataSource"ref="dataSource"/></bean><!-- 通知 --><tx:adviceid="txAdvice"transaction-manager="transactionManager"><tx:attributes><!-- 传播行为 --><tx:methodname="save*"propagation="REQUIRED"/><tx:methodname="insert*"propagation="REQUIRED"/><tx:methodname="add*"propagation="REQUIRED"/><tx:methodname="create*"propagation="REQUIRED"/><tx:methodname="delete*"propagation="REQUIRED"/><tx:methodname="update*"propagation="REQUIRED"/><tx:methodname="find*"propagation="SUPPORTS"read-only="true"/><tx:methodname="select*"propagation="SUPPORTS"read-only="true"/><tx:methodname="get*"propagation="SUPPORTS"read-only="true"/></tx:attributes></tx:advice><!-- 切面 --><aop:config><aop:advisoradvice-ref="txAdvice"pointcut="execution(* cn.e3mall.sso.service..*.*(..))"/></aop:config>

Redis configuration in spring profile 3

<!-- 连接redis单机版 --><beanid="jedisClientPool"class="cn.e3mall.common.jedis.JedisClientPool"><propertyname="jedisPool"ref="jedisPool"></property></bean><beanid="jedisPool"class="redis.clients.jedis.JedisPool"><!-- 一定要用name,构造方法太多用index容易错 --><constructor-argname="host"value="192.168.25.128"/><constructor-argname="port"value="6379"/></bean>

The spring profile configuration: publish service, component scan, and finish the service.

In web. Xml.

<!-- 加载spring容器 --><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/applicationContext*.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>

Configuration database connection in profile db. Properties.

jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/e3mall_32?characterEncoding=utf-8jdbc.username=root
jdbc.password=123456

Set token expiration time in resource. Properties.

SESSION_EXPIRE=1800

E3 sso web introduction jar package
Pom. Xml.

<groupId>cn.e3mall</groupId><artifactId>e3-sso-web</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><dependencies><dependency><groupId>cn.e3mall</groupId><artifactId>e3-sso-interface</artifactId><version>0.0.1-SNAPSHOT</version> 
 </dependency><!-- JSP相关 --><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId></dependency><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><scope>provided</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jsp-api</artifactId><scope>provided</scope></dependency><!-- Spring --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jms</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId></dependency><!-- dubbo相关 --><dependency><groupId>com.alibaba</groupId><artifactId>dubbo</artifactId><exclusions><exclusion><groupId>org.springframework</groupId><artifactId>spring</artifactId></exclusion><exclusion><groupId>org.jboss.netty</groupId><artifactId>netty</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId></dependency><dependency><groupId>com.github.sgroschupf</groupId><artifactId>zkclient</artifactId></dependency></dependencies><!-- 配置tomcat插件 --><build><plugins><plugin><!-- 配置Tomcat插件 --><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><configuration><path>/</path><!-- 表示访问时候不带工程名的 --><port>8088</port></configuration></plugin></plugins></build>

In the spring configuration file, the service is referenced outside of the component scan, but the service layer doesn't publish the service, so it's given that the jsp pages.

In web. Xml.

<!-- 解决post乱码 --><filter><filter-name>CharacterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param></filter><filter-mapping><filter-name>CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!-- springmvc的前端控制器 --><servlet><servlet-name>e3-manager</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation, springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/springmvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>e3-manager</servlet-name><!-- 伪静态化,搜索引擎优化,搜索引擎会先找静态页面 --><url-pattern>/</url-pattern></servlet-mapping>

Attribute profile resource. Properties.

TOKEN_KEY=token

function
Services layer: e3 sso service

  • Receive parameters, determine the correct username and password
  • The user name and password are correct, and the token is generated, equivalent to jsessionid at tomcat. It's generated with uuid and guaranteed uniqueness.
  • Save user information to redis. Key is"session"+ token, value string converted to user information for the query.
  • Setting the validity period of key. Generally half an hour.
  • Returns the e3result that wraps the token
/*
 * 用户登录处理
 */@ServicepublicclassLoginServiceImplimplementsLoginService{@Autowiredprivate TbUserMapper userMapper;
 @Autowiredprivate JedisClient jedisClient;
 @Value("${SESSION_EXPIRE}")
 private Integer SESSION_EXPIRE;
 public E3Result userLogin(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(!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())){
 //2.如果不正确返回登录失败return E3Result.build(400, "用户名或者密码错误");
 }
 //3.如果正确生成token String token = UUID.randomUUID().toString();//生成的uuid必不重复//4.把用户信息写入redis,key:token,value:用户信息 user.setPassword(null);
 jedisClient.set("SESSION:"+ token, JsonUtils.objectToJson(user));
 //5.设置Session的过期时间 jedisClient.expire("SESSION:"+ token, SESSION_EXPIRE);
 //6.把token返回return E3Result.ok(token);
 }
}

Note: if you don't have a mybatis project, take a look at 2 hours so that the code above is clearer.

E3result is a custom response class, as follows

publicclassE3ResultimplementsSerializable{//定义jackson对象privatestaticfinal ObjectMapper MAPPER = new ObjectMapper();
 //响应业务状态private Integer status;
 //响应消息private String msg;
 //响应中的数据private Object data;
 publicstatic E3Result build(Integer status, String msg, Object data) {
 returnnew E3Result(status, msg, data);
 }
 publicstatic E3Result ok(Object data) {
 returnnew E3Result(data);
 }
 publicstatic E3Result ok() {
 returnnew E3Result(null);
 }
 publicE3Result() {
 }
 publicstatic E3Result build(Integer status, String msg) {
 returnnew E3Result(status, msg, null);
 }
 publicE3Result(Integer status, String msg, Object data) {
 this.status = status;
 this.msg = msg;
 this.data = data;
 }
 publicE3Result(Object data) {
 this.status = 200;
 this.msg = "OK";
 this.data = data;
 }
 get、set方法
 }

After the service is written, it needs to be published ( using the dubbo publishing service, zookeeper as the registry )
Profile 4: component scan, publishing service

<context:component-scanbase-package="cn.e3mall.sso.service"/><!-- 使用dubbo发布服务 --><!-- 提供方应用信息,用于计算依赖关系 --><dubbo:applicationname="e3-sso"/><dubbo:registryprotocol="zookeeper"address="192.168.25.128:2181"/><!-- 用dubbo协议在20880端口暴露服务 --><dubbo:protocolname="dubbo"port="20883"/><!-- 一个服务对应一个端口 --><!-- 声明需要暴露的服务接口 --><dubbo:serviceinterface="cn.e3mall.sso.service.LoginService" 
 ref="loginServiceImpl"timeout="600000"/><dubbo:service>

Then install the e3 sso project to the local repository

Presentation layer: e3 sso web
To receive a service, the spring profile

<!-- 加载配置文件 --><context:property-placeholderlocation="classpath:conf/resource.properties"/><context:component-scanbase-package="cn.e3mall.sso.controller"/><mvc:annotation-driven/><beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"><propertyname="prefix"value="/WEB-INF/jsp/"/><propertyname="suffix"value=".jsp"/></bean><mvc:resourceslocation="/css/"mapping="/css/**"/><mvc:resourceslocation="/js/"mapping="/js/**"/><mvc:resourceslocation="/images/"mapping="/images/**"/><!-- 引用dubbo服务 --><dubbo:applicationname="e3-sso-web"/><dubbo:registryprotocol="zookeeper"address="192.168.25.128:2181"/> 
 <dubbo:referenceinterface="cn.e3mall.sso.service.LoginService"id="loginService"/>

Process the process:
1. Incoming user name and password, call service layer. Get e3result.
2. Determine whether login succeeded based on status.
3. Fetch the token information from the obtained e3result and save it to the cookie.
4. Return results to the page.

/*
 * 用户登录处理
 */@ControllerpublicclassLoginController {@Autowiredprivate LoginService loginService;
 @Value("${TOKEN_KEY}")
 private String TOKEN_KEY;//TOKEN_KEY=token@RequestMapping("/page/login")
 public String toLogin(String redirect,Model model){
 model.addAttribute("redirect", redirect);
 //返回登录页面return"login";
 }
 @RequestMapping(value="/user/login",method=RequestMethod.POST)
 @ResponseBodypublic E3Result login(String username,String password,
 HttpServletRequest request,HttpServletResponse response){
 E3Result result = loginService.userLogin(username, password);
 //判断是否登录成功if(result.getStatus()==200){
 String token = (String) result.getData();
 //如果登录成功,token写入cookie  CookieUtils.setCookie(request, response, TOKEN_KEY, token);
 }
 return result;
 }
}

Note: cookies are generally not ( a distributed architecture is used by the marketplace, so the domain names for each module must be different, for example, the above e3 sso port number is ), but the setcookie ( ) method for the cookie tool class cookieutils is handled in the ( ) method of the cookie class.

privatestaticfinalvoiddoSetCookie(HttpServletRequest request, HttpServletResponse response,
 String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
 try {
 if (cookieValue == null) {
 cookieValue = "";
 } elseif (isEncode) {
 cookieValue = URLEncoder.encode(cookieValue, "utf-8");
 }
 Cookie cookie = new Cookie(cookieName, cookieValue);
 if (cookieMaxage> 0)
 cookie.setMaxAge(cookieMaxage);
 if (null!= request) {//设置域名的cookie String domainName = getDomainName(request);
 System.out.println(domainName);
 if (!"localhost".equals(domainName)) {
 cookie.setDomain(domainName);
 }
 }
 cookie.setPath("/");
 response.addCookie(cookie);
 } catch (Exception e) {
 e.printStackTrace();
 }
 }

Here's the project, even if it's done.
After the user logs in, the verification is performed first, and then the token is generated, as key, as the key, the user information is stored as value and. And returns the token to the presentation layer, and the presentation layer stores token into the cookie. When the user accesses other modules, such as the order module, we can write the interceptor, intercept the request, determine whether the user is logged in, from the Otherwise release.

For example, accessing a shopping cart system ( Tmall access shopping cart requires logon status ):
Writing interceptor in e3 order web project

/*
 * 用户登录处理
 */publicclassLoginInterceptorimplementsHandlerInterceptor {@Autowiredprivate TokenService tokenService;
 publicbooleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
 throws Exception {
 //前处理,执行handler之前执行此方法//返回true:放行 false:拦截//1.从cookie中取token String token = CookieUtils.getCookieValue(request, "token");
 //2.如果没有token,未登录状态if(StringUtils.isBlank(token)){
 returntrue;
 }
 //3.如果取到token,需要调用sso系统的服务,根据token取用户信息 E3Result e3Result = tokenService.getUserByToken(token);
 if (e3Result.getStatus()!=200){
 //4.没有取到用户信息,登录已经过期,直接放行returntrue;
 }
 //5.取到用户信息。登录状态。 TbUser user = (TbUser) e3Result.getData();
 //6.把用户信息放到request中,只需要在controller中判断request中是否包含user信息。 request.setAttribute("user", user);
 returntrue;
 }
 publicvoidpostHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
 ModelAndView modelAndView) throws Exception {
 //handler执行之后,返回modelAndView之前 }
 publicvoidafterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
 throws Exception {
 //完成处理,返回modelAndView之后(已经响应了)//可以再次处理异常 }
}

Note: here you need to publish a new service in the sso system

<dubbo:service interface="cn.e3mall.sso.service.TokenService" 
 ref="tokenServiceImpl" timeout="600000"/>

The service is based on the token.

/*
 * 根据token取用户信息
 */@ServicepublicclassTokenServiceImplimplementsTokenService{@Autowiredprivate JedisClient jedisClient;
 @Value("${SESSION_EXPIRE}")
 private Integer SESSION_EXPIRE;
 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 user = JsonUtils.jsonToPojo(json, TbUser.class);
 return E3Result.ok(user); 
 }
}

Then refer to the service in the e3 cart web project

 <dubbo:reference interface="cn.e3mall.sso.service.TokenService"id="tokenService"/>

Test:
这里写图片描述
Click login. Prompt to log on to the home page after the login is successful.
View redis
这里写图片描述
这里写图片描述




Copyright © 2011 Dowemo All rights reserved.    Creative Commons   AboutUs