SpringBoot 整合Shrio 缓存redis webSocket - Go语言中文社区

SpringBoot 整合Shrio 缓存redis webSocket


个人整合  有问题请评论 及时修改   共同进步

能查找这个整合的都是有基础的    就直接干货

项目目录

springboot的启动文件创建项目时都生成  略过

maven   的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.test</groupId>
	<artifactId>SpringBootshiro</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringBootshiro</name>
	<description>Spring Boot test 3</description>

	<properties>
		<java.version>1.8</java.version>
		
	</properties>

	<dependencies>
		<!--SpringBoot Web支持 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!--SpringBoot 测试支持 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- websocket 依赖 -->
		<dependency>  
           <groupId>org.springframework.boot</groupId>  
           <artifactId>spring-boot-starter-websocket</artifactId>  
       </dependency>
		<!--添加tomcat支持 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-thymeleaf</artifactId>
		    <version>2.1.6.RELEASE</version>
		</dependency>

		<!-- reids -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
		<!-- 阿里Json 包 -->
		<dependency>
		    <groupId>com.alibaba</groupId>
		    <artifactId>fastjson</artifactId>
		    <version>1.2.58</version>
		</dependency>
		<!-- 安全框架shiro 支持 -->
		<dependency>
		    <groupId>org.apache.shiro</groupId>
		    <artifactId>shiro-spring</artifactId>
		    <version>1.4.1</version>
		</dependency>
		<!-- shiro 缓存框架 -->
		<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!-- shiro+redis缓存插件 -->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>2.4.2.1-RELEASE</version>
        </dependency>
        <!-- shiro 标签支持 -->
        <dependency>
		    <groupId>com.github.theborakompanioni</groupId>
		    <artifactId>thymeleaf-extras-shiro</artifactId>
		    <version>2.0.0</version>
		</dependency>
		<!-- mybatis 支持 -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.0.0</version>
		</dependency>
		<!-- oracle 支持 -->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.3</version>
		</dependency>
		<!-- 获取硬件信息状态 -->
		<dependency>
		    <groupId>org.fusesource</groupId>
		    <artifactId>sigar</artifactId>
		    <version>1.6.4</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <!--打包的时候, 略过test, 不运行-->
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
		</plugins>
	</build>

</project>

配置文件  application.properties

#内置tomcat 配置 
server.port=80
server.tomcat.uri-encoding=utf-8

spring.main.allow-bean-definition-overriding=true

# 数据源连接
spring.datasource.driver-class-name: oracle.jdbc.driver.OracleDriver
spring.datasource.url: jdbc:oracle:thin:@127.0.0.1:1521:TEST
spring.datasource.username: arch
spring.datasource.password: manager

#thymeleaf 组件配置
spring.thymeleaf.cache=false
#spring.thymeleaf.prefix=classpath:/templates/*
#spring.thymeleaf.check-template-location=true
#spring.thymeleaf.suffix=.html
#spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
#spring.thymeleaf.servlet.content-type=text/html

# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=manager
# 连接超时时间(毫秒)
spring.redis.timeout=0

#mybatis配置文件路径
mybatis.mapper-locations=classpath:mappers/*.xml

shiro的配置类

package com.test.config;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.test.shiro.CustomRealm;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;

@Configuration
public class ShiroConfig {
	
	@Bean(name = "shiroFilter")
	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //登录页面
        shiroFilterFactoryBean.setLoginUrl("/login.html");
        //未授权页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/error/403.html");
        //登录成功页面
        //shiroFilterFactoryBean.setSuccessUrl("/index.html");
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/sock.html", "anon");
        filterChainDefinitionMap.put("/helloSocket/**", "user");
        //拦截其他所有路径和页面
        filterChainDefinitionMap.put("/**", "user");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
	}
	
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        //设置realm
        defaultSecurityManager.setRealm(customRealm());
        //自定义缓存实现    redis
        defaultSecurityManager.setCacheManager(cacheManager());
        //自定义session管理     redis 
        defaultSecurityManager.setSessionManager(sessionManager());
        //记住我  自定义配置
        defaultSecurityManager.setRememberMeManager(rememberMeManager());
        return defaultSecurityManager;
    }

    @Bean
    public CustomRealm customRealm() {
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return customRealm;
    }
    
    /**
     * 		加密
     * @return
     */
	@Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 散列的次数,比如散列两次,相当于 md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(1024);
        return hashedCredentialsMatcher;
    }
	
	/**
     * cacheManager 缓存 redis实现
     *	 使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     *	 配置shiro redisManager
     * 	使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("localhost");
        redisManager.setPort(6379);
        redisManager.setExpire(1800);// 配置缓存过期时间
        redisManager.setTimeout(0);
        redisManager.setPassword("manager");
        return redisManager;
    }
    
    /**
     * Session Manager
     * 	使用的是shiro-redis开源插件
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        sessionManager.setDeleteInvalidSessions(true);//删除过期session
        sessionManager.setGlobalSessionTimeout(24 * 60 * 60 * 1000);//session过期时间
        sessionManager.setSessionIdCookie(rememberMeCookie());
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 	使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }
    
    @Bean
    public SimpleCookie rememberMeCookie(){
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setHttpOnly(true);
        //<!-- 记住我cookie生效时间1天 ,单位秒;-->
        //simpleCookie.setMaxAge(24 * 60 * 60);
        simpleCookie.setMaxAge(10);
        return simpleCookie;
    }
    
    @Bean
    public CookieRememberMeManager rememberMeManager(){
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey("ZHANKXIAOHEI_WST".getBytes());
        return cookieRememberMeManager;
    }
    
    @Bean
    public ShiroDialect shiroDialect() {
		return new ShiroDialect();
    }
}

shiro的登录处理

package com.test.shiro;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.test.entity.User;
import com.test.service.IUserService;

import java.util.*;
@Component("authenticator")
public class CustomRealm extends AuthorizingRealm {
	private static Logger logger = LoggerFactory.getLogger(CustomRealm.class);
	
	@Autowired
	private IUserService userServic;
	/**
	 * 权限认证
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		logger.info("--------------------权限认证------------------------------------------------");
		// TODO Auto-generated method stub
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		User user = (User) principals.getPrimaryPrincipal();
		System.out.println("权限" + user);
		Set<String> roles = new HashSet<>();
		
		
		return authorizationInfo;
	}
	
	/**
	 * 用户认证
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		logger.info("--------------------用户认证------------------------------------------------");
		SimpleAuthenticationInfo info = null;
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		String username = upToken.getUsername();
		//从数据库中获取用户数据
		User user = userServic.AccountLogin(username);
		System.out.println(user);
		if(user==null) {
			throw new UnknownAccountException("用户不存在!");
		}
		if(user.getUt_forbidden().equals(0)) {
			throw new LockedAccountException("用户被锁定");
		}
		//shiro对比密码
		info = new SimpleAuthenticationInfo(user,user.getUt_passwrod(),ByteSource.Util.bytes(user.getUt_account()),getName());
		return info;
	}
	
	public static void main(String[] args) {
		String hashAlgorithmName = "MD5";
		Object credentials = "123456";
		Object salt = ByteSource.Util.bytes("admin");
		int hashIterations = 1024;
		
		Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
		System.out.println(result);
	}
}

从数据库中获取 用户信息  和权限信息自行处理    (我本地有oracle数据库我就用的这个)

注:如果不开启缓存,没出现权限的认证都会去数据库中调取数据。虽说这样权限是实时的权限,但是会大大的增加数据库的消耗

登录的控制层

package com.test.Controller;

import java.io.IOException;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.test.entity.User;
import com.test.service.IUserService;
@RestController
@RequestMapping("/user")
public class UserController {
	@Autowired
	private IUserService userServic;
	
	@RequestMapping("/login")
	public String AccountLogin(HttpSession session,String username,String password,String rem, HttpServletResponse response) {
		Subject currentUser = SecurityUtils.getSubject();
		if (!currentUser.isAuthenticated()) {
			UsernamePasswordToken token = new UsernamePasswordToken(username, password);
			if(rem == null) {
				token.setRememberMe(false);
			}else {
				token.setRememberMe(true);
			}
            try {
            	currentUser.login(token);
            	User user = (User) currentUser.getPrincipal();
            	System.out.println(user);
            	session.setAttribute("user", user);
            	try {
					response.sendRedirect("/");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}catch(UnknownAccountException ue) {
				System.out.println(ue.getMessage());
				return ue.getMessage();
			}catch(IncorrectCredentialsException e) {
				System.out.println("账户名或密码错误");
				return "账户名或密码错误";
			}catch (AuthenticationException e) {
				System.out.println("登录失败"+ e.getMessage());
				return e.getMessage();
			}
		}
		return "redirect:/";
		
	}
}

这里的记住我没有配置成自动的

shiro的前台登录页面

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
	
	<h4>Login Page</h4>
	
	<form action="user/login" method="POST">
		username: <input type="text" name="username"/>
		<br><br>
		
		password: <input type="password" name="password"/>
		<br><br>
		
		<input type="checkbox" value="true" name="rem" >记住我
		<br><br>
		<input type="submit" value="Submit"/>
	</form>
	
</body>
</html>

一切从简

登录后的动态页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
session:<h1 th:text="${session.user.ut_name}"></h1>

Welcome: <shiro:principal property="ut_name"></shiro:principal>
<shiro:hasRole name="admin">
	<br><br>
	<a href="sock.html">Admin Page</a>
	</shiro:hasRole>
	
	
	<br><br>
	<a href="/logout">Logout</a>
</body>
</html>

注:html的标签的属性一定要写上   要不shiro的标签用不了


shiro的配置示例就结束了      下面在框架中加入websocket的配置


 

websocket的配置文件

package com.test.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
	
	@Bean  
    public ServerEndpointExporter serverEndpointExporter() {  
        return new ServerEndpointExporter();  
    } 
}

写个简单的测试

package com.test.socket;

import java.io.IOException;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.springframework.stereotype.Component;

@Component
@ServerEndpoint(value = "/helloSocket/{userid}")
public class MyWebSocketServer {
	Session session ;
	String userid;
    /***
     * 当建立链接时,调用的方法.
     * @param session
     * @throws IOException 
     */
    @OnOpen
    public void open(Session session , @PathParam("userid") String userid) throws IOException {
        System.out.println("当前session的id是:" + session.getId());
        System.out.println(userid);
        this.session = session;
        this.userid = userid;
        session.getBasicRemote().sendText("连接唱功");
        WebSocketMapUtil.put(userid, this);
        WebSocketMapUtil.sendAll(userid + "登录");
        
    }
    
    /***
     * 处理消息的方法.
     * @param session
     */
    @OnMessage
    public void message(Session session, String data) {
    	System.out.println("当前session的id是:" + session.getId());
    	System.out.println("从前端页面传过来的数据是:" + data);
    }
    
    @OnClose
    public void close(Session session) {
        System.out.println("退出的id是:" + session.getId() +" id:"+userid);
        WebSocketMapUtil.remove(userid);
        WebSocketMapUtil.sendAll(userid + "退出");
    }
    /**
     * 向客户端发送信息
     */
    public void sendMessage(String msg) {
    	try {
			session.getBasicRemote().sendText(msg);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
}
package com.test.socket;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;


public class WebSocketMapUtil {

public static Map<String, MyWebSocketServer> webSocketMap = new HashMap<>();

public static void put(String key, MyWebSocketServer myWebSocketServer){
     webSocketMap.put(key, myWebSocketServer);
    }

    public static MyWebSocketServer get(String key){
         return webSocketMap.get(key);
    }

    public static void remove(String key){
         webSocketMap.remove(key);
    }
 
    public static Collection<MyWebSocketServer> getValues(){
        return webSocketMap.values();
    }
    
    public static void sendAll(String msg) {
    	//session.getBasicRemote().sendText("连接唱功");
    	System.out.println("当前在线人数:" + webSocketMap.size());
    	for(Map.Entry<String, MyWebSocketServer> entry: webSocketMap.entrySet()) {
    		//System.out.println("在线用户" + entry.getKey());
    		try {
				entry.getValue().session.getBasicRemote().sendText(msg);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}
    }
}

前台测试页面


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Charles-WebSocket</title>
<script type="text/javascript" src="jquery.min.js"></script>

<script type="text/javascript">
    
    var websocket = null;
    var target = "/helloSocket/";
    
    function buildConnection(){
    	var sendmsg = document.getElementById("userid").value;
    	if('WebSocket' in window) {
            websocket = new WebSocket("ws://" + window.location.host +target + sendmsg);        
        } else if('MozWebSocket' in window) {
            websocket = MozWebSocket("ws://" + window.location.host +target + sendmsg);
        } else {
            window.alert("浏览器不支持WebSocket");
        }
		websocket.onmessage = function(ev){  //获取后端响应
			console.log(ev.data);
			$("#content").append(ev.data+"<br/>");
		};
		websocket.onclose = function(ev){
			console.log("close");
		};
		websocket.onerror = function(ev){
			console.log("error");
		};
    	
    }
    
    // 往后台服务器发送消息.
    function sendMessage() {
        var sendmsg = document.getElementById("sendMsg").value;
        console.log("发送的消息:" + sendmsg);
        
        // 发送至后台服务器中.
        websocket.send(sendmsg);
    }
   
    
</script>
</head>
<body>
    <input id="userid"><button οnclick="buildConnection();">开始建立链接</button>
	<hr>
	<div  id="content"  style="
		border: 1px solid black; width: 400px; height: 300px;
		float: left;
	"  ></div>
	<div  id="userList"  style="
		border: 1px solid black; width: 100px; height: 300px;
		float:left;
	"  ></div>

	<div  style="clear: both;" >
		<input id="sendMsg"  /><button οnclick="sendMessage();"  >send</button>
	</div>
</body>
</html>

项目源码的百度网盘

链接:https://pan.baidu.com/s/1U3xDd8aIuREMOAHrFNM5_g 
提取码:nqiv 

有条件的csdn上支持一下5积分

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_36219549/article/details/98742789
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-09-05 16:25:57
  • 阅读 ( 1096 )
  • 分类:Redis

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢