Springboot+redis实现单点登陆(SSO)
2020-01-15 10:59
1386 查看
Springboot+redis实现单点登陆(SSO)
1.单点登录介绍
简介:
SSO英文全称Single Sign On。
单点登录是一种控制多个相关但彼此独立的系统访问权限的机制, 拥有这一权限的用户。
可以使用单一的ID和密码访问某个或多个系统从而避免使用不同的用户名或密码,或者通过某种配置无缝地登录每个系统,它是目前比较流行的企业业务整合的解决方案之一,例如我们使用mis号登陆过公司的一个系统后,再登陆其他系统不用再次输入用户名和密码
2.单点登录流程图
3.代码正文
因为该技术需要用到Redis缓存,所以大家需要先安装好redis工具
Redis官网:http://www.redis.cn/
- 依赖包:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- 引入druid数据源--> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.9</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.20</version> <scope>provided</scope> </dependency> <!--引入thymeleaf--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> </dependencies>
- application,yml
spring: redis: host: xxx.xxx.xxx.xxx port: 6379 password: jedis: pool: max-active: 8 max-idle: 500 min-idle: 0 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/goods?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource #initialization-mode: always #数据源其他配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 mybatis: #config-location: classpath:mybatis/mybatis-config.xml mapper-locations: classpath:mybatis/mapper/*.xml REDIS_KEY : student
- 实体类:
import lombok.Data; import java.io.Serializable; @Data public class User implements Serializable { private int id; private String username; private String password; }
- Dao层:
@Repository @Mapper public interface UserMapper { //登录方法 @Select("select * from user where username=#{username}") public User login(String username); }
- service层:
用到了md5的一个加密
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; import java.util.UUID; @Service public class UserService { private static Logger logger = LoggerFactory.getLogger(UserService.class); @Autowired UserMapper userMapper; @Autowired private RedisTemplate redisTemplate; private Map<Integer,String> userInfo = new HashMap<>(); @Value("${REDIS_KEY}") //从配置文件中取值 private String REDIS_KEY; public User userlogin(HttpServletRequest request, HttpServletResponse response, User u) { //字符串的序列化器 RedisSerializer redisSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(redisSerializer); //先进行数据库查询一遍 User us=userMapper.login(u.getUsername()); //判断us是否为空 if(us==null){ return null; } String pass=string2MD5(u.getPassword()); System.out.println(pass); System.out.println(us.getPassword()); if(!pass.equals(us.getPassword())){ return null; } //定义新的token String token="user_"+ UUID.randomUUID().toString(); //判断map中是否存在该id if(!ObjectUtils.isEmpty(userInfo.get(us.getId()))){ //从map中获得redis中的key String oldToken=userInfo.get(us.getId()); //删除redis中老的值 redisTemplate.delete(oldToken); } //将新的的key保存到map中 userInfo.put(us.getId(),token); //将信息存入redis redisTemplate.opsForValue().set(token,us); //设置redis信息过期时间 // redisTemplate.expire(token,5*60, TimeUnit.MILLISECONDS); //将token放入cookie中 CookieUtils.setCookie(request,response,"USER_TOKEN",token,5*60,true); return us; } public User getUserByToken(HttpServletResponse response, HttpServletRequest request) { User us=null; //从cookie中取出用户token String token=CookieUtils.getCookieValue(request,"USER_TOKEN"); User o = (User)redisTemplate.opsForValue().get(token); return o; } /*** * MD5加码 生成32位md5码 */ public String string2MD5(String inStr){ MessageDigest md5 = null; try{ md5 = MessageDigest.getInstance("MD5"); }catch (Exception e){ System.out.println(e.toString()); e.printStackTrace(); return ""; } char[] charArray = inStr.toCharArray(); byte[] byteArray = new byte[charArray.length]; for (int i = 0; i < charArray.length; i++) byteArray[i] = (byte) charArray[i]; byte[] md5Bytes = md5.digest(byteArray); StringBuffer hexValue = new StringBuffer(); for (int i = 0; i < md5Bytes.length; i++){ int val = ((int) md5Bytes[i]) & 0xff; if (val < 16) hexValue.append("0"); hexValue.append(Integer.toHexString(val)); } return hexValue.toString(); } }
- Controller层:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; @Controller public class loginController { @Autowired UserService userService; @RequestMapping("Login.html") public String returnLogin(){ return "Login"; } @RequestMapping("Index.html") public String returnIndex(){ return "Index"; } @PostMapping("login") public String login(HttpServletRequest req, HttpServletResponse res, User user, Model model){ try{ User u=userService.userlogin(req,res,user); if(u==null){ model.addAttribute("error","用户名或密码不匹配"); return "Login.html"; } return "redirect:Index.html"; }catch (Exception e){ e.printStackTrace(); model.addAttribute("error","用户名或密码不匹配"); return "Login.html"; } } @GetMapping("info") @ResponseBody public Map getUserInfo(HttpServletRequest req,HttpServletResponse res){ Map<Object,Object>map=new HashMap<>(); try { User us=userService.getUserByToken(res,req); if(us!=null){ map.put("success",true); }else { map.put("success",false); } return map; } catch (Exception e) { map.put("success","根据token获取用户信息失败"); return map; } } }
- 工具类
CookieUtils:
import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; /** * * Cookie 工具类 * */ public final class CookieUtils { /** * 得到Cookie的值, 不编码 * * @param request * @param cookieName * @return */ public static String getCookieValue(HttpServletRequest request, String cookieName) { return getCookieValue(request, cookieName, false); } /** * 得到Cookie的值, * * @param request * @param cookieName * @return */ public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) { Cookie[] cookieList = request.getCookies(); if (cookieList == null || cookieName == null) { return null; } String retValue = null; try { for (int i = 0; i < cookieList.length; i++) { if (cookieList[i].getName().equals(cookieName)) { if (isDecoder) { retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8"); } else { retValue = cookieList[i].getValue(); } break; } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return retValue; } /** * 得到Cookie的值, * * @param request * @param cookieName * @return */ public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) { Cookie[] cookieList = request.getCookies(); if (cookieList == null || cookieName == null) { return null; } String retValue = null; try { for (int i = 0; i < cookieList.length; i++) { if (cookieList[i].getName().equals(cookieName)) { retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString); break; } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return retValue; } /** * 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码 */ public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue) { setCookie(request, response, cookieName, cookieValue, -1); } /** * 设置Cookie的值 在指定时间内生效,但不编码 */ public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage) { setCookie(request, response, cookieName, cookieValue, cookieMaxage, false); } /** * 设置Cookie的值 不设置生效时间,但编码 */ public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, boolean isEncode) { setCookie(request, response, cookieName, cookieValue, -1, isEncode); } /** * 设置Cookie的值 在指定时间内生效, 编码参数 */ public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) { doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode); } /** * 设置Cookie的值 在指定时间内生效, 编码参数(指定编码) */ public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, String encodeString) { doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString); } /** * 删除Cookie带cookie域名 */ public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String cookieName) { doSetCookie(request, response, cookieName, "", -1, false); } /** * 设置Cookie的值,并使其在指定时间内生效 * * @param cookieMaxage cookie生效的最大秒数 */ private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) { try { if (cookieValue == null) { cookieValue = ""; } else if (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(); } } /** * 设置Cookie的值,并使其在指定时间内生效 * * @param cookieMaxage cookie生效的最大秒数 */ private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, String encodeString) { try { if (cookieValue == null) { cookieValue = ""; } else { cookieValue = URLEncoder.encode(cookieValue, encodeString); } 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(); } } /** * 得到cookie的域名 */ private static final String getDomainName(HttpServletRequest request) { String domainName = null; String serverName = request.getRequestURL().toString(); if (serverName == null || serverName.equals("")) { domainName = ""; } else { final int end = serverName.lastIndexOf("/"); serverName = serverName.substring(0, end); final String[] domains = serverName.split("\\."); int len = domains.length; if (len > 3) { // www.xxx.com.cn domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1]; } else if (len <= 3 && len > 1) { // xxx.com or xxx.cn domainName = "." + domains[len - 2] + "." + domains[len - 1]; } else { domainName = serverName; } } if (domainName != null && domainName.indexOf(":") > 0) { String[] ary = domainName.split("\\:"); domainName = ary[0]; } return domainName; } }
JsonUtils:
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; /** * 自定义相应结构 */ public class JsonUtils { //定义jackson对象 private static final ObjectMapper MAPPER=new ObjectMapper(); /** * 将对象转换成json字符串。 * <p>Title: pojoToJson</p> * <p>Description: </p> * @param data * @return */ public static String objectToJson(Object data) { try { String string = MAPPER.writeValueAsString(data); return string; } catch (JsonProcessingException e) { e.printStackTrace(); } return null; } /** * 将json结果集转化为对象 * * @param jsonData json数据 * @param beanType 对象中的object类型 * @return */ public static <T> T jsonToPojo(String jsonData, Class<T> beanType) { try { T t = MAPPER.readValue(jsonData, beanType); return t; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 将json数据转换成pojo对象list * <p>Title: jsonToList</p> * <p>Description: </p> * @param jsonData * @param beanType * @return */ public static <T> List<T> jsonToList(String jsonData, Class<T> beanType) { JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType); try { List<T> list = MAPPER.readValue(jsonData, javaType); return list; } catch (Exception e) { e.printStackTrace(); } return null; } }
3.HTML页面
主要是对功能的实现 页面就相对简单了一点
- Index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1 align="center">首页</h1> </body> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script type="text/javascript"> $(function () { alert("0") $.get("info",function (data) { if(data.success==false){ alert("您的账号已经在别处登录!"); location.href="Login.html"; } }) }) </script> </html>
- Login.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div align="center"> <form method="post" action="login"> 用户名:<input type="text" name="username"><br/> 密码:<input type="password" name="password"><br/> <input type="submit" value="登录"> <input type="reset" value="取消"><br/> <span th:text="${error}"></span> </form> </div> </body> </html>
- 点赞
- 收藏
- 分享
- 文章举报
相关文章推荐
- spring-boot-shiro-jwt-redis实现登陆授权功能
- Springboot+Redis 实现基本缓存
- SpringBoot中使用Redis实现缓存
- springboot整合redis,实现session共享
- spring+springmvc+Interceptor+jwt+redis实现sso单点登录
- Spring Boot项目利用Redis实现session管理
- Spring-session+Redis 实现SSO其源码简析和注意事项
- Springboot实现redis键失效监控操作
- SpringBoot之整合redis实现缓存
- SpringBoot+Vue+Redis实现单点登录(一处登录另一处退出登录)
- Spring Boot 整合 Redis 实现缓存操作
- springBoot +SpringSession+Redis实现sessionId共享
- spring boot + redis 实现session共享
- springboot+redis实现session共享
- 使用springboot+redis实现session共享
- spring boot + redis 实现session共享
- springboot+redis实现session共享
- springboot 整合shiro + cas 实现单点登录权限管理(一)(sso)
- SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例
- Spring Boot项目利用Redis实现session管理实例