Quellcode durchsuchen

系统管理测试

laowo vor 4 Jahren
Ursprung
Commit
e647b2f221

+ 5 - 0
pom.xml

@@ -63,6 +63,11 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-thymeleaf</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.github.theborakompanioni</groupId>
+            <artifactId>thymeleaf-extras-shiro</artifactId>
+            <version>2.0.0</version>
+        </dependency>
         <!--test-->
         <dependency>
             <groupId>org.springframework.boot</groupId>

+ 10 - 10
src/main/java/com/usky/config/CorsConfig.java

@@ -34,14 +34,14 @@ public class CorsConfig implements WebMvcConfigurer {
 
 
     }
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    @Bean
-    @Order(1)//设置filter执行的顺序
-    public FilterRegistrationBean filterRegest(){
-        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
-        filterRegistrationBean.setFilter(new XssFilter());
-        filterRegistrationBean.addUrlPatterns("/*");//过滤所有的URL
-// filterRegistrationBean.setOrder(1);/another way to set the sequence
-        return filterRegistrationBean;
-    }
+//    @SuppressWarnings({ "rawtypes", "unchecked" })
+//    @Bean
+//    @Order(1)//设置filter执行的顺序
+//    public FilterRegistrationBean filterRegest(){
+//        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
+//        filterRegistrationBean.setFilter(new XssFilter());
+//        filterRegistrationBean.addUrlPatterns("/*");//过滤所有的URL
+//// filterRegistrationBean.setOrder(1);/another way to set the sequence
+//        return filterRegistrationBean;
+//    }
 }

+ 104 - 0
src/main/java/com/usky/config/ShiroProperties.java

@@ -0,0 +1,104 @@
+package com.usky.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author laowo
+ * @version v1.0
+ * @date 2021/8/19 17:24
+ * @description TODO
+ **/
+@Data
+@ConfigurationProperties(prefix = "shiro")
+@Component
+public class ShiroProperties {
+    /**
+     * shiro 常规设置
+     */
+    private User user = new User();
+    /**
+     * cookie设置
+     */
+    private Cookie cookie = new Cookie();
+    /**
+     * session设置
+     */
+    private Session session = new Session();
+
+    @Data
+    public static class User {
+        /**
+         * 登录地址
+         */
+        private String loginUrl;
+        /**
+         * 权限认证失败地址
+         */
+        private String unauthorizedUrl;
+        /**
+         * 首页地址
+         */
+        private String indexUrl;
+        /**
+         * 验证码开关
+         */
+        private Boolean captchaEnabled;
+        /**
+         * 验证码类型
+         */
+        private String captchaType;
+    }
+
+    @Data
+
+    public static class Cookie {
+        /**
+         * # 设置Cookie的域名 默认空,即当前访问的域名
+         */
+        private String domain;
+        /**
+         * 设置cookie的有效访问路径
+         */
+        private String path;
+        /**
+         * 设置HttpOnly属性
+         */
+        private Boolean httpOnly;
+        /**
+         * 设置Cookie的过期时间,天为单位
+         */
+        private int maxAge;
+        /**
+         * cipherKey
+         */
+        private String cipherKey;
+
+    }
+
+    @Data
+    static class Session {
+        /**
+         * Session超时时间,-1代表永不过期(默认30分钟)
+         */
+        private int expireTime;
+        /**
+         * 同步session到数据库的周期(默认1分钟)
+         */
+        private int dbSyncPeriod;
+        /**
+         * 相隔多久检查一次session的有效性,默认就是10分钟
+         */
+        private int validationInterval;
+        /**
+         * 同一个用户最大会话数,比如2的意思是同一个账号允许最多同时两个人登录(默认-1不限制)
+         */
+        private int maxSession;
+        /**
+         * 踢出之前登录的/之后登录的用户,默认踢出之前登录的用户
+         */
+        private boolean kickoutAfter;
+    }
+}

+ 52 - 8
src/main/java/com/usky/config/redis/RedisConfig.java

@@ -3,12 +3,16 @@ package com.usky.config.redis;
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.PropertyAccessor;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.usky.config.shiro.MyRedisSerializer;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
 import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
 import org.springframework.data.redis.serializer.RedisSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
@@ -35,8 +39,35 @@ public class RedisConfig {
 	 */
 	@Bean
 	public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
+//		log.info(" --- redis config init --- ");
+//		Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =jacksonSerializer();
+//		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
+//		redisTemplate.setConnectionFactory(lettuceConnectionFactory);
+//		RedisSerializer<?> stringSerializer = new StringRedisSerializer();
+//		// key序列化
+//		redisTemplate.setKeySerializer(stringSerializer);
+//		// value序列化
+//		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
+//		// Hash key序列化
+//		redisTemplate.setHashKeySerializer(stringSerializer);
+//		// Hash value序列化
+//		redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
+//		redisTemplate.afterPropertiesSet();
+//		return redisTemplate;
+
+//		MyRedisSerializer myRedisSerializer = new MyRedisSerializer();
+//		// 7.设置 value 的转化格式和 key 的转化格式 默认使用的是JdkSerializationRedisSerializer
+//		template.setValueSerializer(myRedisSerializer);
+//		template.setHashValueSerializer(myRedisSerializer);
+//		// 设置键(key)的序列化采用StringRedisSerializer。
+//		template.setKeySerializer(new StringRedisSerializer());
+//		template.setHashKeySerializer(new StringRedisSerializer());
+//		template.setDefaultSerializer(myRedisSerializer);
+//		template.afterPropertiesSet();
+//		return template;
+
 		log.info(" --- redis config init --- ");
-		Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =jacksonSerializer();
+		MyRedisSerializer jackson2JsonRedisSerializer = new MyRedisSerializer();
 		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
 		redisTemplate.setConnectionFactory(lettuceConnectionFactory);
 		RedisSerializer<?> stringSerializer = new StringRedisSerializer();
@@ -50,14 +81,27 @@ public class RedisConfig {
 		redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
 		redisTemplate.afterPropertiesSet();
 		return redisTemplate;
+
+
+
+
+
 	}
-	private Jackson2JsonRedisSerializer jacksonSerializer() {
-		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
-		ObjectMapper objectMapper = new ObjectMapper();
-		objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
-		objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
-		jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
-		return jackson2JsonRedisSerializer;
+//	private Jackson2JsonRedisSerializer jacksonSerializer() {
+//		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+//		ObjectMapper objectMapper = new ObjectMapper();
+//		objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+//		objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+//		jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
+//		return jackson2JsonRedisSerializer;
+//	}
+
+	@Bean
+	@ConditionalOnMissingBean(StringRedisTemplate.class)
+	public RedisTemplate<String, String> MystringRedisTemplate(RedisConnectionFactory factory) {
+		return new StringRedisTemplate(factory);
 	}
 
+
+
 }

+ 46 - 52
src/main/java/com/usky/config/shiro/MyRealm.java

@@ -1,6 +1,8 @@
 package com.usky.config.shiro;
 
 
+import com.usky.entity.sys.SysUserDTO;
+import com.usky.service.user.LoginService;
 import org.apache.shiro.authc.*;
 import org.apache.shiro.authz.AuthorizationInfo;
 import org.apache.shiro.authz.SimpleAuthorizationInfo;
@@ -8,62 +10,54 @@ import org.apache.shiro.realm.AuthorizingRealm;
 import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.util.ByteSource;
 import org.apache.shiro.util.SimpleByteSource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 
 import java.util.Set;
 
 /**
- * @author laowo
- * @version v1.0
- * @date 2020/11/3 10:32
- * @description 自定义Realm
- **/
+ * 和数据库交互 看看用户信息 权限信息
+ */
+public class MyRealm extends AuthorizingRealm {
+    @Lazy
+    @Autowired
+    private LoginService loginService;
 
-//public class MyRealm extends AuthorizingRealm {
-//    Logger logger = LoggerFactory.getLogger(MyRealm.class);
-//    @Autowired
-//    @Lazy
-//    private UserService userServiceImpl;
-//
-//    /*
-//     * 授权逻辑
-//     * */
-//    @Override
-//    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
-//        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
-//        //获取用户名
-//        TbUserEntity userEntity = (TbUserEntity) principalCollection.getPrimaryPrincipal();//(认证时构造)
-//        String username = userEntity.getUserName();
-//        //从数据库查询用户的权限和角色
-//        //角色查询
-//        Set<String> roles = userServiceImpl.queryUseRoleByUserName(username);
-//        if (roles != null && roles.size() > 0) {
-//            simpleAuthorizationInfo.addRoles(roles);
-//        }
-//        //权限查询
-//        Set<String> permissions = userServiceImpl.findPermissionsByUsername(username);
-//        if (permissions != null && permissions.size() > 0) {
-//            simpleAuthorizationInfo.addStringPermissions(permissions);
-//        }
-//        return simpleAuthorizationInfo;
-//    }
-//
-//    /*
-//     * 认证逻辑
-//     * */
-//    @Override
-//    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
-//        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
-//        String username = token.getUsername();
-//        TbUserEntity userEntity = userServiceImpl.queryUserByUserName(username);
-//        if (userEntity == null) {
-//            return null;
-//        }
-//        ByteSource byteSource = new SimpleByteSource(userEntity.getPrivateSalt());
-//        AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userEntity, userEntity.getPassword(), byteSource, getName());
-//        return authenticationInfo;
-//    }
-//}
+    //获取权限信息的方法
+    @Override
+    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+        System.out.println("走了权限查询方法");
+        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
+        //获取用户名
+        SysUserDTO administrator = (SysUserDTO) principals.getPrimaryPrincipal();//此Principal就是彼Principal(认证时构造的)
+        String username = administrator.getLoginName();
+        //从数据库查询用户的权限和角色
+        // Set<String> roles = loginService.findRolesByUsername(username);
+        //   if(roles != null && roles.size() > 0){
+        //       simpleAuthorizationInfo.addRoles(roles);
+        //   }
+        //   Set<String> permissions = adminService.findPermissionsByUsername(username);
+        //   if(permissions != null && permissions.size() > 0){
+        //       simpleAuthorizationInfo.addStringPermissions(permissions);
+        //   }
+        //   return simpleAuthorizationInfo;
+        return null;
+    }
+
+    //获取认证信息的方法
+    @Override
+    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+        //token是封装好的用户提交的用户名密码
+        String username = ((UsernamePasswordToken) token).getUsername();
+        //获取用户
+        SysUserDTO user = loginService.findUserByUsername(username);
+        if (user == null) {
+            return null;
+        } else {
+            //封装AuthenticationInfo
+            ByteSource bsSalt = new SimpleByteSource(user.getSalt());
+            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), bsSalt, getName());
+            return authenticationInfo;
+        }
+    }
+}

+ 43 - 0
src/main/java/com/usky/config/shiro/MyRedisSerializer.java

@@ -0,0 +1,43 @@
+package com.usky.config.shiro;
+
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.SerializationException;
+ 
+import java.io.*;
+ 
+/**
+ * 重写序列化 序列化为字节码
+ */
+public class MyRedisSerializer implements RedisSerializer {
+ 
+ 
+    @Override
+    public byte[] serialize(Object o) throws SerializationException {
+        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+        ObjectOutputStream objOut;
+        try {
+            objOut = new ObjectOutputStream(byteOut);
+            objOut.writeObject(o);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return byteOut.toByteArray();
+    }
+ 
+    @Override
+    public Object deserialize(byte[] bytes) throws SerializationException {
+        if(bytes == null) return null;
+        ByteArrayInputStream byteIn = new ByteArrayInputStream(bytes);
+        ObjectInputStream objIn;
+        Object obj;
+        try {
+            objIn = new ObjectInputStream(byteIn);
+            obj =objIn.readObject();
+        } catch (IOException | ClassNotFoundException e) {
+            e.printStackTrace();
+            return null;
+        }
+        return obj;
+    }
+ 
+}

+ 125 - 72
src/main/java/com/usky/config/shiro/ShiroConfig.java

@@ -1,13 +1,19 @@
 package com.usky.config.shiro;
 
+import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
 import org.apache.shiro.authc.credential.CredentialsMatcher;
 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
+import org.apache.shiro.cache.CacheManager;
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.realm.Realm;
+import org.apache.shiro.session.mgt.SessionManager;
+import org.apache.shiro.session.mgt.eis.SessionDAO;
 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -17,45 +23,45 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 
 /**
- * @author laowo
- * @version v1.0
- * @date 2020/11/3 9:20
- * @description TODO
- **/
-
-
-//@Configuration
-//public class ShiroConfig {
-//    //创建ShiroFilterFactoryBean
-//    @Bean
-//    public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
-//        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
-//        //设置安全管理器
-//        shiroFilterFactoryBean.setSecurityManager(securityManager());
-//        /*
-//        anon 无需认证访问
-//        authc 认证访问
-//        perms 授权访问
-//        role 角色授权访问
-//         */
-//        Map<String, String> hashMap = new LinkedHashMap<>();
-//        shiroFilterFactoryBean.setLoginUrl("/");
-//        hashMap.put("/login/toLogin", "anon");
-//        hashMap.put("/", "anon");
-//        //swagger2
-//        hashMap.put("/swagger-ui.html", "anon");
-//        hashMap.put("/swagger-resources", "anon");
-//        hashMap.put("/swagger-resources/configuration/security", "anon");
-//        hashMap.put("/swagger-resources/configuration/ui", "anon");
-//        hashMap.put("/v2/api-docs", "anon");
-//        hashMap.put("/webjars/springfox-swagger-ui/**", "anon");
-//        //  hashMap.put("/user/queryUserAll", "anon");
-//        //  hashMap.put("/*", "authc");
-//        hashMap.put("/login/loginOut", "logout");
-//        shiroFilterFactoryBean.setFilterChainDefinitionMap(hashMap);
-//        return shiroFilterFactoryBean;
-//    }
-//    //创建DefaultWebSecurityManager
+ *
+ */
+@Configuration
+public class ShiroConfig {
+    //shiroFilter
+    @Bean
+    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
+        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
+        shiroFilterFactoryBean.setSecurityManager(securityManager);
+        shiroFilterFactoryBean.setLoginUrl("/page/toLogin");
+
+        //控制 访问xx资源 需要xx权限
+        Map filterChainMap = new LinkedHashMap<String,String>();
+        filterChainMap.put("/sys/login","anon"); //访问登录页面 直接放行
+        filterChainMap.put("/","anon"); //访问登录页面 直接放行
+        filterChainMap.put("/user/all","perms[user:select]"); //查询所有用户 需要认证(登录)
+
+        //当用户查看仓库列表时,需要有仓库权限
+        filterChainMap.put("/storage/all","perms[storage:select]");
+        //当用户删除用户时,需要有超级管理员角色
+//        filterChainMap.put("/user/del/*","roles[role_superman]");
+
+        filterChainMap.put("/backend/logout","logout");
+
+        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
+        return shiroFilterFactoryBean;
+    }
+
+    //安全管理器
+    @Bean
+    @Lazy
+    public SecurityManager securityManager() {
+        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+        securityManager.setSessionManager(sessionManager());
+        securityManager.setRealm(myRealm());
+        return securityManager;
+    }
+
+
 //    @Bean("SecurityManager")
 //    public SecurityManager securityManager() {
 //        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
@@ -65,36 +71,83 @@ import java.util.Map;
 //        defaultWebSecurityManager.setRealm(getRealm());
 //        return defaultWebSecurityManager;
 //    }
-//    @Bean(name = "Realm")
-//    @Lazy
-//    public Realm getRealm() {
-//        MyRealm myRealm = new MyRealm();
-//        //设置密码匹配器
-//        myRealm.setCredentialsMatcher(credentialsMatcher());
-//        return myRealm;
-//    }
-//    //创建密码匹配器
-//    @Bean
-//    public CredentialsMatcher credentialsMatcher() {
-//        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
-//        hashedCredentialsMatcher.setHashAlgorithmName("md5");
-//        hashedCredentialsMatcher.setHashIterations(1);
-//        return hashedCredentialsMatcher;
-//    }
-//    /**
-//     * 注解支持:
-//     */
-//    @Bean
-//    @ConditionalOnMissingBean
-//    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
-//        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
-//        defaultAAP.setProxyTargetClass(true);
-//        return defaultAAP;
-//    }
-//    @Bean
-//    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
-//        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
-//        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
-//        return authorizationAttributeSourceAdvisor;
-//    }
-//}
+
+    //realm
+    @Bean
+    public Realm myRealm(){
+        MyRealm myRealm = new MyRealm();
+        //告诉realm密码匹配方式
+        myRealm.setCredentialsMatcher(credentialsMatcher());
+        myRealm.setAuthorizationCacheName("perms");
+        myRealm.setAuthorizationCachingEnabled(true);
+        myRealm.setAuthenticationCachingEnabled(false);
+        //设置缓存管理器
+        myRealm.setCacheManager(MycacheManager());
+        return myRealm;
+    }
+
+    //缓存管理
+    @Bean
+    public CacheManager MycacheManager(){
+        MyRedisCacheManager cacheManager = new MyRedisCacheManager();
+        return cacheManager;
+    }
+
+    @Bean
+    public CredentialsMatcher credentialsMatcher(){
+        HashedCredentialsMatcher hashedMatcher = new HashedCredentialsMatcher();
+        hashedMatcher.setHashAlgorithmName("md5");
+//        hashedMatcher.setHashIterations(1);
+        return hashedMatcher;
+    }
+
+    /**
+     * 注解支持
+     */
+    @Bean
+    @ConditionalOnMissingBean
+    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
+        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
+        defaultAAP.setProxyTargetClass(true);
+        return defaultAAP;
+    }
+
+    @Bean
+    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
+        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
+        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
+        return authorizationAttributeSourceAdvisor;
+    }
+
+    @Bean
+    public ShiroDialect shiroDialect(){
+        return new ShiroDialect();
+    }
+
+    /**
+     * 会话管理器
+     * @return
+     */
+    @Bean
+    public SessionManager sessionManager() {
+        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
+        sessionManager.setSessionDAO(redisSessionDAO());
+
+        //设置会话过期时间
+        sessionManager.setGlobalSessionTimeout(3*60*1000); //默认半小时
+        sessionManager.setDeleteInvalidSessions(true); //默认自定调用SessionDAO的delete方法删除会话
+        //设置会话定时检查
+        //        sessionManager.setSessionValidationInterval(180000); //默认一小时
+        //        sessionManager.setSessionValidationSchedulerEnabled(true);
+        return sessionManager;
+    }
+
+    @Bean
+    public SessionDAO redisSessionDAO(){
+        ShiroRedisSessionDao redisDAO = new ShiroRedisSessionDao();
+        return redisDAO;
+    }
+
+
+
+}

+ 58 - 0
src/main/java/com/usky/controller/login/LoginController.java

@@ -0,0 +1,58 @@
+package com.usky.controller.login;
+
+import com.usky.entity.sys.SysUserDTO;
+import com.usky.service.user.UserService;
+import com.usky.utils.Result;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationToken;
+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.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author laowo
+ * @version v1.0
+ * @date 2021/8/19 17:09
+ * @description TODO
+ **/
+@RestController
+@RequestMapping("sys")
+@Api(tags = "登录")
+public class LoginController {
+    @PostMapping("login")
+    @ApiOperation(value = "用户登录")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "username", value = "登录名", required = true, paramType = "query"),
+            @ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query")
+    })
+    public Result<?> login(String username, String password, Boolean rememberMe) {
+//        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
+//        Subject subject = SecurityUtils.getSubject();
+//        try {
+//            subject.login(token);
+//            return Result.OK();
+//        } catch (AuthenticationException e) {
+//            String msg = "用户或密码错误";
+//            if (StringUtils.isNotEmpty(e.getMessage())) {
+//                msg = e.getMessage();
+//            }
+//            return Result.error(msg);
+//        }
+//    }
+
+        // subject - securityManager - realm
+        Subject subject = SecurityUtils.getSubject();
+        AuthenticationToken token = new UsernamePasswordToken(username, password);
+        subject.login(token);
+        return Result.OK("登录成功");
+    }
+}

+ 31 - 0
src/main/java/com/usky/controller/user/UserController.java

@@ -0,0 +1,31 @@
+package com.usky.controller.user;
+
+import com.usky.entity.sys.SysUserDTO;
+import com.usky.service.user.UserService;
+import com.usky.utils.Result;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author laowo
+ * @version v1.0
+ * @date 2021/8/20 15:12
+ * @description TODO
+ **/
+@Api(tags = "用户管理")
+@RestController
+@RequestMapping("sys/user")
+public class UserController {
+    @Autowired
+    private UserService userService;
+    @ApiOperation(value = "系统-用户添加")
+    @PostMapping("addUser")
+    public Result<?> addUser(SysUserDTO user) {
+        userService.addUser(user);
+        return Result.OK();
+    }
+}

+ 3 - 1
src/main/java/com/usky/entity/sys/SysUserDTO.java

@@ -3,6 +3,7 @@ package com.usky.entity.sys;
 import io.swagger.annotations.ApiModel;
 
 import javax.persistence.*;
+import java.io.Serializable;
 import java.sql.Timestamp;
 import java.util.Objects;
 
@@ -15,7 +16,8 @@ import java.util.Objects;
 
 @Entity
 @Table(name = "sys_user", schema = "jx_cover", catalog = "")
-public class SysUserDTO {
+public class SysUserDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
     private long userId;
     private Long deptId;
     private String loginName;

+ 25 - 0
src/main/java/com/usky/exception/BaseException.java

@@ -0,0 +1,25 @@
+package com.usky.exception;
+
+public class BaseException extends RuntimeException
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final String message;
+
+    public BaseException(String message)
+    {
+        this.message = message;
+    }
+
+    public BaseException(String message, Throwable e)
+    {
+        super(message, e);
+        this.message = message;
+    }
+
+    @Override
+    public String getMessage()
+    {
+        return message;
+    }
+}

+ 33 - 9
src/main/java/com/usky/exception/GloableExceptionResolver.java

@@ -1,14 +1,18 @@
 package com.usky.exception;
 
+import com.usky.utils.Result;
+import com.usky.utils.ServletUtils;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.shiro.authz.UnauthenticatedException;
 import org.apache.shiro.authz.UnauthorizedException;
 import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.servlet.ModelAndView;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -23,7 +27,8 @@ import java.util.Map;
  * @date 2020/11/10 15:00
  * @description 全局异常处理
  **/
-@ControllerAdvice
+@RestControllerAdvice
+@Slf4j
 public class GloableExceptionResolver {
 
     @ExceptionHandler(UnauthorizedException.class)
@@ -81,6 +86,25 @@ public class GloableExceptionResolver {
                 writer.close();
         }
     }
+
+
+    @ExceptionHandler(BaseException.class)
+    public Object businessException(HttpServletRequest request, BaseException e)
+    {
+        log.error(e.getMessage(), e);
+        if (ServletUtils.isAjaxRequest(request))
+        {
+            return Result.error(e.getMessage());
+        }
+        else
+        {
+            ModelAndView modelAndView = new ModelAndView();
+            modelAndView.addObject("errorMessage", e.getMessage());
+            modelAndView.setViewName("error/business");
+            return modelAndView;
+        }
+    }
+
     /**
      * 处理所有未知的异常
      */
@@ -96,11 +120,11 @@ public class GloableExceptionResolver {
     /**
      * 处理IO异常
      */
-    @ExceptionHandler(IOException.class)
-    @ResponseStatus(HttpStatus.BAD_REQUEST)
-    public Map<String, String> validationExceptionHandler(IOException exception) {
-        Map<String, String> map = new HashMap<>(1);
-        map.put("message", "IO异常");
-        return map;
-    }
+//    @ExceptionHandler(IOException.class)
+//    @ResponseStatus(HttpStatus.BAD_REQUEST)
+//    public Map<String, String> validationExceptionHandler(IOException exception) {
+//        Map<String, String> map = new HashMap<>(1);
+//        map.put("message", "IO异常");
+//        return map;
+//    }
 }

+ 16 - 0
src/main/java/com/usky/service/user/LoginService.java

@@ -0,0 +1,16 @@
+package com.usky.service.user;
+
+import com.usky.entity.sys.SysUserDTO;
+
+/**
+ * @author laowo
+ */
+public interface LoginService {
+    /**
+     * 根据登录用户名查用户
+     *
+     * @param username
+     * @return
+     */
+    SysUserDTO findUserByUsername(String username);
+}

+ 31 - 0
src/main/java/com/usky/service/user/LoginServiceImpl.java

@@ -0,0 +1,31 @@
+package com.usky.service.user;
+
+
+import com.usky.dao.impl.BaseDaoImpl;
+import com.usky.entity.sys.SysUserDTO;
+import com.usky.exception.user.UserPasswordNotMatchException;
+import com.usky.utils.ListUtil;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * @author laowo
+ * @version v1.0
+ * @date 2021/8/20 11:22
+ * @description TODO
+ **/
+@Service
+public class LoginServiceImpl extends BaseDaoImpl implements LoginService {
+    @Override
+    @SuppressWarnings("unchecked")
+    public SysUserDTO findUserByUsername(String username) {
+
+        List<SysUserDTO> list = getSession().createQuery("from SysUserDTO where loginName ='" + username + "'").list();
+        if (ListUtil.isBlank(list)) {
+            throw new UserPasswordNotMatchException();
+        }
+        return list.get(0);
+    }
+}

+ 15 - 0
src/main/java/com/usky/service/user/UserService.java

@@ -0,0 +1,15 @@
+package com.usky.service.user;
+
+import com.usky.entity.sys.SysUserDTO;
+
+/**
+ * @author laowo
+ */
+public interface UserService {
+    /**
+     * 用户添加
+     *
+     * @param user
+     */
+    void addUser(SysUserDTO user);
+}

+ 30 - 0
src/main/java/com/usky/service/user/UserServiceImpl.java

@@ -0,0 +1,30 @@
+package com.usky.service.user;
+
+import com.usky.dao.impl.BaseDaoImpl;
+import com.usky.entity.sys.SysUserDTO;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.shiro.crypto.hash.Md5Hash;
+import org.springframework.stereotype.Service;
+
+import java.sql.Timestamp;
+
+/**
+ * @author laowo
+ * @version v1.0
+ * @date 2021/8/20 15:08
+ * @description TODO
+ **/
+@Service
+public class UserServiceImpl extends BaseDaoImpl implements UserService {
+    @Override
+    public void addUser(SysUserDTO user) {
+        String password = user.getPassword();
+        String salt = RandomStringUtils.randomNumeric(6, 8);
+        user.setSalt(salt);
+        Md5Hash md5Hash = new Md5Hash(password, salt); //模拟md5加密一次
+        user.setPassword(md5Hash.toString());
+        user.setStatus("0");
+        user.setCreateTime(new Timestamp(System.currentTimeMillis()));
+        getSession().save(user);
+    }
+}

+ 35 - 0
src/main/java/com/usky/utils/CipherUtils.java

@@ -0,0 +1,35 @@
+package com.usky.utils;
+
+import javax.crypto.KeyGenerator;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * 对称密钥密码算法工具类
+ *
+ */
+public class CipherUtils
+{
+    /**
+     * 生成随机秘钥
+     *
+     * @param keyBitSize 字节大小
+     * @param algorithmName 算法名称
+     * @return 创建密匙
+     */
+    public static Key generateNewKey(int keyBitSize, String algorithmName)
+    {
+        KeyGenerator kg;
+        try
+        {
+            kg = KeyGenerator.getInstance(algorithmName);
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            String msg = "Unable to acquire " + algorithmName + " algorithm.  This is required to function.";
+            throw new IllegalStateException(msg, e);
+        }
+        kg.init(keyBitSize);
+        return kg.generateKey();
+    }
+}

+ 63 - 0
src/main/java/com/usky/utils/ServletUtils.java

@@ -0,0 +1,63 @@
+package com.usky.utils;
+
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.persistence.Convert;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+
+import static org.apache.commons.lang3.StringUtils.trim;
+
+/**
+ * 客户端工具类
+ */
+public class ServletUtils {
+    /**
+     * 是否是Ajax异步请求
+     *
+     * @param request
+     */
+    public static boolean isAjaxRequest(HttpServletRequest request) {
+        String accept = request.getHeader("accept");
+        if (accept != null && accept.indexOf("application/json") != -1) {
+            return true;
+        }
+
+        String xRequestedWith = request.getHeader("X-Requested-With");
+        if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) {
+            return true;
+        }
+
+        String uri = request.getRequestURI();
+        if (inStringIgnoreCase(uri, ".json", ".xml")) {
+            return true;
+        }
+
+        String ajax = request.getParameter("__ajax");
+        if (inStringIgnoreCase(ajax, "json", "xml")) {
+            return true;
+        }
+        return false;
+    }
+
+    public static boolean inStringIgnoreCase(String str, String... strs)
+    {
+        if (str != null && strs != null)
+        {
+            for (String s : strs)
+            {
+                if (str.equalsIgnoreCase(trim(s)))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+
+}

+ 69 - 0
src/main/java/com/usky/utils/ShiroUtils.java

@@ -0,0 +1,69 @@
+package com.usky.utils;
+
+import com.usky.entity.sys.SysUserDTO;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.crypto.SecureRandomNumberGenerator;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.SimplePrincipalCollection;
+import org.apache.shiro.subject.Subject;
+import org.springframework.beans.BeanUtils;
+
+public class ShiroUtils {
+    public static Subject getSubject() {
+        return SecurityUtils.getSubject();
+    }
+
+    public static Session getSession() {
+        return SecurityUtils.getSubject().getSession();
+    }
+
+    public static void logout() {
+        getSubject().logout();
+    }
+
+    public static SysUserDTO getSysUser() {
+        SysUserDTO user = null;
+        Object obj = getSubject().getPrincipal();
+        if (null != null) {
+            user = new SysUserDTO();
+            BeanUtils.copyProperties(user,obj);
+        }
+        return user;
+    }
+
+    public static void setSysUser(SysUserDTO user) {
+        Subject subject = getSubject();
+        PrincipalCollection principalCollection = subject.getPrincipals();
+        String realmName = principalCollection.getRealmNames().iterator().next();
+        PrincipalCollection newPrincipalCollection = new SimplePrincipalCollection(user, realmName);
+        // 重新加载Principal
+        subject.runAs(newPrincipalCollection);
+    }
+
+    public static Long getUserId() {
+        return getSysUser().getUserId();
+    }
+
+    public static String getLoginName() {
+        return getSysUser().getLoginName();
+    }
+
+    public static String getIp() {
+        return getSubject().getSession().getHost();
+    }
+
+    public static String getSessionId() {
+        return String.valueOf(getSubject().getSession().getId());
+    }
+
+    /**
+     * 生成随机盐
+     */
+    public static String randomSalt() {
+        // 一个Byte占两个字节,此处生成的3字节,字符串长度为6
+        SecureRandomNumberGenerator secureRandom = new SecureRandomNumberGenerator();
+        String hex = secureRandom.nextBytes(3).toHex();
+        return hex;
+    }
+}

+ 35 - 2
src/main/resources/application.yml

@@ -72,7 +72,7 @@ mqtt:
       producer-enable: true
    #   url: [tcp://124.71.175.91:1883]
       url: [tcp://47.98.201.73:1883]
-      topics: [/usky/10012/861050040560669/#,/usky/10012/861050040560321/#,/usky/10012/861050040533286/#]
+      topics: [/1usky/10012/861050040560669/#,/1usky/10012/861050040560321/#,/1usky/10012/861050040533286/#]
       qos: [0,0,0]
   #   username: wjzn2021
   #   password: wjzn2021
@@ -94,7 +94,40 @@ mqtt:
         topic: will_topic
         payload: '{"id": "producer_client_test1"}'
         retained: false
-
+shiro:
+  user:
+    # 登录地址
+    loginUrl: /login
+    # 权限认证失败地址
+    unauthorizedUrl: /unauth
+    # 首页地址
+    indexUrl: /index
+    # 验证码开关
+    captchaEnabled: true
+    # 验证码类型 math 数组计算 char 字符
+    captchaType: math
+  cookie:
+    # 设置Cookie的域名 默认空,即当前访问的域名
+    domain:
+    # 设置cookie的有效访问路径
+    path: /
+    # 设置HttpOnly属性
+    httpOnly: true
+    # 设置Cookie的过期时间,天为单位
+    maxAge: 30
+    # 设置密钥,务必保持唯一性(生成方式,直接拷贝到main运行即可)Base64.encodeToString(CipherUtils.generateNewKey(128, "AES").getEncoded()) (默认启动生成随机秘钥,随机秘钥会导致之前客户端RememberMe Cookie无效,如设置固定秘钥RememberMe Cookie则有效)
+    cipherKey:
+  session:
+    # Session超时时间,-1代表永不过期(默认30分钟)
+    expireTime: 30
+    # 同步session到数据库的周期(默认1分钟)
+    dbSyncPeriod: 1
+    # 相隔多久检查一次session的有效性,默认就是10分钟
+    validationInterval: 10
+    # 同一个用户最大会话数,比如2的意思是同一个账号允许最多同时两个人登录(默认-1不限制)
+    maxSession: -1
+    # 踢出之前登录的/之后登录的用户,默认踢出之前登录的用户
+    kickoutAfter: false