spring boot结合shiro实现用户-角色-权限的控制(包含用户名密码登陆和手机号验证码登陆)

【Springboot+Vue】做一个权限管理后台(八):shiro实现手机号免密登陆 效果展示 前端设计 主要在登录界面增加了手机号登录: <template> <div class="login-container"> <el-tabs v-model="activeName" type="card"> <el-tab-pane label="账号/密码登陆" name="name"> ... 阅读详情

spring boot整合shiro实现权限校验

1.首先导入项目所需jar包

   <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/>
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
        <mysql.version>5.1.47</mysql.version>
        <mybaitsPlus.version>3.1.1</mybaitsPlus.version>
        <druid.version>1.0.31</druid.version>
        <shiro-redis.version>3.1.0</shiro-redis.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--devtools热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
            <scope>runtime</scope>
        </dependency>

        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.1</version>
        </dependency>
        <!--redis存储-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
  
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
            <scope>compile</scope>
        </dependency>
        <!-- Shiro-redis插件 -->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>${shiro-redis.version}</version>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session</artifactId>
            <version>1.3.5.RELEASE</version>
        </dependency>
        <dependency>
			<groupId>com.sun</groupId>
			<artifactId>tools</artifactId>
			<version>1.8.0</version>
			<scope>system</scope>
			<systemPath>${env.JAVA_HOME}/lib/tools.jar</systemPath>
			<optional>true</optional>
		</dependency>
        <!-- 分页插件 -->
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>
        </plugins>
    </build>

2. 编写yml和properties配置文件

yml文件

server:
  port: 8083
  #tomcat:
  #max-http-post-size: -1
  max-http-header-size: 4048576
spring:
  profiles:
    active: diagnose
  application:
    name: user-center
  devtools:
    restart:
      enabled: true  #   设置开启热部署
      additional-paths: src/main/java #  重启目录
      exclude: WEB-INF/**
  freemarker:
    cache: false    #  页面不加载缓存,修改即时生效
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.1.199/zhzd?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root

#  myBatis-plus
mybatis-plus:
  #configuration:
    #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #  输出sql日志和查询结果日志
  mapper-locations: classpath*:mapper/**Mapper.xml     #  .xml目录
  type-aliases-package: com.etouch.mapper          #  接口包
  global-config:
    db-config:
      db-type: mysql
      column-underline=true: #  默认驼峰

# 只打印sql日志和sql参数
logging:
  file: logs/sys.log
  level:
    com:
      etouch:
        mapper: debug

# shiro自定义配置
shiro:
  session:
    jsessionid: x-auth-token
  role:
    superRole: superAdmin

---
#   Redis数据源
spring:
  profiles: diagnose
  redis:
    host: localhost
    port: 6379
    timeout: 6000
    password: null
    database: 0
    jedis:
      pool:
        max-active: 1000  #   连接池最大连接数(使用负值表示没有限制)
        max-wait: -1      #   连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 10      #   连接池中的最大空闲连接
        min-idle: 5       #   连接池中的最小空闲连接
eureka:
  instance:
    prefer-ip-address: true
    instance-id: user-center
    lease-expiration-duration-in-seconds: 15
    lease-renewal-interval-in-seconds: 5
  client:
    service-url:
      defaultZone:  http://eureka:eureka@localhost:8800/eureka/
    registry-fetch-interval-seconds: 5

properties文件


#redis 中存储的用户登录数据结构
user.login.redis=zhzd:user:login:
#redis 中存储的验证码结构
user.send.code=zhzd:user:code:
#redis 中存储的用户信息
user.info.dto=zhzd:user:login:dto:

3.新增MyShiroRealm继承AuthorizingRealm,

package com.etouch.config.shiro;

import com.etouch.DTO.MenuDTO;
import com.etouch.DTO.RoleDTO;
import com.etouch.DTO.UserDTO;
import com.etouch.corecenter.utils.UuidUtils;
import com.etouch.entity.SysMenu;
import com.etouch.entity.SysRole;
import com.etouch.entity.SysUser;
import com.etouch.entity.SysUserToken;
import com.etouch.service.SysUserTokenService;
import com.etouch.service.UserService;
import com.etouch.utils.ShiroUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

public class MyShiroRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;

    /**
     * 权限校验
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserDTO user = (UserDTO) SecurityUtils.getSubject().getPrincipal();
        UserDTO userDTO = userService.findUserByName(user.getLoginName());
        for (RoleDTO role : userDTO.getRoleList()) {
            authorizationInfo.addRole(role.getRoleName());
            for (MenuDTO menu : role.getMenuDTOList()) {
                authorizationInfo.addStringPermission(menu.getMenuUrl());
            }
        }
        return authorizationInfo;
    }

    /**
     * 登录认证
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //强转成自己传入的参数,要不然获取不到自己传入的用户名和密码
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        //获得前台输入的用户名和密码
        String username = upToken.getUsername();
        String password = new String(upToken.getPassword());
        //根据邮箱获得数据库的用户
        UserDTO user = userService.findUserByName(username);
        if (user == null) {
            return null;  //return null 在底层会抛出异常
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
        return info;
    }

  /*  // 清除缓存
    public void clearCached() {
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        super.clearCache(principals);
    }*/

4. 新增动态获取权限, 校验权限业务类

接口类

package com.etouch.config.shiro.service;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;

import java.util.Map;

/**
 *  <p> shiro权限处理 </p>
 */
public interface ShiroService {

    /**
     * 初始化权限 -> 拿全部权限
     *
     * @param :
     * @return: java.util.Map<java.lang.String,java.lang.String>
     */
    Map<String, String> loadFilterChainDefinitionMap();

    /**
     * 在对uri权限进行增删改操作时,需要调用此方法进行动态刷新加载数据库中的uri权限
     *
     * @param shiroFilterFactoryBean
     * @param roleId
     * @param isRemoveSession:
     * @return: void
     */
    void updatePermission(ShiroFilterFactoryBean shiroFilterFactoryBean, String roleId, Boolean isRemoveSession);

    /**
     * shiro动态权限加载 -> 原理:删除shiro缓存,重新执行doGetAuthorizationInfo方法授权角色和权限
     *
     * @param roleId
     * @param isRemoveSession:
     * @return: void
     */
    void updatePermissionByRoleId(String roleId, Boolean isRemoveSession);

}

实现类

package com.etouch.config.shiro.service.impl;

import com.etouch.config.shiro.service.ShiroService;
import com.etouch.entity.SysMenu;
import com.etouch.entity.SysUser;
import com.etouch.exception.ZhException;
import com.etouch.mapper.SysMenuMapper;
import com.etouch.mapper.SysRoleMapper;
import com.etouch.mapper.SysUserMapper;
import com.etouch.utils.ShiroUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

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

/**
 * <p> shiro权限处理实现类 </p>
 *
 * @description:
 * @author: zhengqing
 * @date: 2019/9/7 0007 13:53
 */
@Slf4j
@Service
public class ShiroServiceImpl implements ShiroService {

    @Autowired
    private SysMenuMapper menuMapper;
    @Autowired
    private SysUserMapper userMapper;

    @Autowired

    @Override
    public Map<String, String> loadFilterChainDefinitionMap() {
        // 权限控制map
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 配置过滤:不会被拦截的链接 -> 放行 start ----------------------------------------------------------
        // 放行Swagger2页面,需要放行这些
        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
        filterChainDefinitionMap.put("/swagger/**", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");

        // 三方登录
//        filterChainDefinitionMap.put("/auth/loginByQQ", "anon");
//        filterChainDefinitionMap.put("/auth/afterlogin.do", "anon");
        // 退出
        // token过期接口
        filterChainDefinitionMap.put("/sysUser/tokenExpired", "anon");
        // 被挤下线
        filterChainDefinitionMap.put("/sysUser/downline", "anon");
        //手机号,验证码登录
        filterChainDefinitionMap.put("/sysUser/login", "anon");
        filterChainDefinitionMap.put("/sysUser/loginByPhone", "anon");
        filterChainDefinitionMap.put("/sysUser/logOut", "anon");

        //管理员可操作所有的
        //发送验证码和校验验证码
        filterChainDefinitionMap.put("/*/sendCode", "anon");
        filterChainDefinitionMap.put("/*/checkoutCode", "anon");
        //swaggerUI
        // 放行 end ----------------------------------------------------------
        //TODO 管理员可以访问任何接口
        filterChainDefinitionMap.put("/**", "roles[superAdmin]");
        // 从数据库或缓存中查取出来的url与resources对应则不会被拦截 放行
        List<SysMenu> permissionList = menuMapper.selectList(null);
        if (!CollectionUtils.isEmpty(permissionList)) {
            permissionList.forEach(e -> {
                if (StringUtils.isNotBlank(e.getMenuUrl())) {
                    filterChainDefinitionMap.put(e.getMenuUrl(), "perms["+e.getMenuUrl()+"]");
//                        filterChainDefinitionMap.put("/api/system/user/listPage", "authc,token,zqPerms[user1]"); // 写死的一种用法
                }
            });
        }
        // ⑤ 认证登录  【注:map不能存放相同key】
        //TODO  开发阶段不需要登录就可以访问
        filterChainDefinitionMap.put("/**", "anon");
        return filterChainDefinitionMap;
    }

    @Override
    public void updatePermission(ShiroFilterFactoryBean shiroFilterFactoryBean, String roleId, Boolean isRemoveSession) {
        synchronized (this) {
            AbstractShiroFilter shiroFilter;
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
            } catch (Exception e) {
                throw new ZhException("get ShiroFilter from shiroFilterFactoryBean error!");
            }
            PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
            DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();

            // 清空拦截管理器中的存储
            manager.getFilterChains().clear();
            // 清空拦截工厂中的存储,如果不清空这里,还会把之前的带进去
            //            ps:如果仅仅是更新的话,可以根据这里的 map 遍历数据修改,重新整理好权限再一起添加
            shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
            // 动态查询数据库中所有权限
            shiroFilterFactoryBean.setFilterChainDefinitionMap(loadFilterChainDefinitionMap());
            // 重新构建生成拦截
            Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                manager.createChain(entry.getKey(), entry.getValue());
            }
            log.info("--------------- 动态生成url权限成功! ---------------");

            // 动态更新该角色相关联的用户shiro权限
            if (roleId != null) {
                updatePermissionByRoleId(roleId, isRemoveSession);
            }
        }
    }

    @Override
    public void updatePermissionByRoleId(String roleId, Boolean isRemoveSession) {
        // 查询当前角色的用户shiro缓存信息 -> 实现动态权限
        List<SysUser> userList = userMapper.selectUserByRoleId(roleId);
        // 删除当前角色关联的用户缓存信息,用户再次访问接口时会重新授权 ; isRemoveSession为true时删除Session -> 即强制用户退出
        if (!CollectionUtils.isEmpty(userList)) {
            for (SysUser user : userList) {
                ShiroUtils.deleteCache(user.getLoginName(), isRemoveSession);
            }
        }
        log.info("--------------- 动态修改用户权限成功! ---------------");
    }

}

5. 新建shiro的配置文件ShiroConfig

package com.etouch.config.shiro;

import java.util.ArrayList;
import java.util.List;

import com.etouch.utils.CustomCredentialsMatcher;
import com.etouch.config.shiro.service.impl.ShiroServiceImpl;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
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.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {

    private final String CACHE_KEY = "shiro:cache:";
    private final String SESSION_KEY = "shiro:session:";
    private final int EXPIRE = 1000*60*60*24*7; //TODO 单位毫秒

    /**
     * Redis配置
     */
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${shiro.session.jsessionid}")
    private String jsessionid;

    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager, ShiroServiceImpl shiroService) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        /*
        * 常用的过滤器如下:

            authc:所有已登陆用户可访问

            roles:有指定角色的用户可访问,通过[ ]指定具体角色,这里的角色名称与数据库中配置一致

            perms:有指定权限的用户可访问,通过[ ]指定具体权限,这里的权限名称与数据库中配置一致

            anon:所有用户可访问,通常作为指定页面的静态资源时使用
        * */
        //未登录提示
        shiroFilterFactoryBean.setLoginUrl("/sysUser/unLogin");
        //没有权限提示
        shiroFilterFactoryBean.setUnauthorizedUrl("/sysUser/unAuth");
        // 权限控制map.

        shiroFilterFactoryBean.setFilterChainDefinitionMap(shiroService.loadFilterChainDefinitionMap());
        return shiroFilterFactoryBean;
    }

    @Bean
    public CustomCredentialsMatcher credentialMatcher() {
        return new CustomCredentialsMatcher();
    }

    // 将自己的验证方式加入容器,注入自定义的realm
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        //将密码比较器注入自定义realm域
        myShiroRealm.setCredentialsMatcher(credentialMatcher());
        myShiroRealm.setCacheManager(cacheManager());
        return myShiroRealm;
    }

    @Bean
    public PhoneRealm phoneRealm() {
        PhoneRealm phoneRealm = new PhoneRealm();
        phoneRealm.setCacheManager(cacheManager());
        return phoneRealm;
    }

    // 权限管理,配置主要是Realm的管理认证

    /**
     * 配置核心安全事务管理器
     *
     * @param shiroRealm
     * @return
     */
    @Bean(name = "securityManager")
    public SecurityManager securityManager(@Qualifier("myShiroRealm") MyShiroRealm shiroRealm, PhoneRealm phoneRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realms
        List<Realm> realms = new ArrayList<>();
        realms.add(shiroRealm);
        realms.add(phoneRealm);
        //设置自定义realm.
        securityManager.setRealms(realms);
        //配置记住我
        securityManager.setRememberMeManager(rememberMeManager());
        // 自定义session管理
        securityManager.setSessionManager(sessionManager());
        // 自定义Cache实现缓存管理
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }

    /**
     * cookie对象;会话Cookie模板 ,默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid或rememberMe,自定义
     *
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:

        //setcookie()的第七个参数
        //设为true后,只能通过http访问,javascript无法访问
        //防止xss读取cookie
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        //<!-- 记住我cookie生效时间30天 ,单位秒;-->
        simpleCookie.setMaxAge(2592000);
        return simpleCookie;
    }

    /**
     * cookie管理对象;记住我功能,rememberMe管理器
     *
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

    // 加入注解的使用,不加入这个注解不生效
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 配置Redis管理器:使用的是shiro-redis开源插件
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setTimeout(timeout);
//        redisManager.setPassword(password);
        return redisManager;
    }

    /**
     * 配置Cache管理器:用于往Redis存储权限和角色标识  (使用的是shiro-redis开源插件)
     */
    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setKeyPrefix(CACHE_KEY);
        // 配置缓存的话要求放在session里面的实体类必须有个id标识 注:这里id为用户表中的主键,否-> 报:User must has getter for field: xx
        redisCacheManager.setPrincipalIdFieldName("id");
        return redisCacheManager;
    }

    /**
     * SessionID生成器
     */
    @Bean
    public ShiroSessionIdGenerator sessionIdGenerator() {
        return new ShiroSessionIdGenerator();
    }

    /**
     * 配置RedisSessionDAO (使用的是shiro-redis开源插件)
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
        redisSessionDAO.setKeyPrefix(SESSION_KEY);
        redisSessionDAO.setExpire(EXPIRE);
        return redisSessionDAO;
    }

    /**
     * 配置Session管理器
     */
    @Bean
    public SessionManager sessionManager() {
        ShiroSessionManager shiroSessionManager = new ShiroSessionManager();
        shiroSessionManager.setGlobalSessionTimeout(EXPIRE);
        shiroSessionManager.setSessionDAO(redisSessionDAO());//修改默认session存储机制,将shiro中的session数据存储至redis数据库中
        shiroSessionManager.setSessionIdCookie(getSessionIdCookie());//设置sessionId的Key
        shiroSessionManager.setDeleteInvalidSessions(true);//自动清空失效session
        return shiroSessionManager;
    }

    /**
     * 给shiro的sessionId默认的JSSESSIONID名字改掉
     * @return
     */
    @Bean(name="sessionIdCookie")
    public SimpleCookie getSessionIdCookie(){
        SimpleCookie simpleCookie = new SimpleCookie(jsessionid);
        return simpleCookie;
    }

    /**
     * 该类如果不设置为static,@Value注解就无效,原因未知
     * @return
     */
    @Bean
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

}
自定义密码比较器
package com.etouch.utils;

import com.etouch.corecenter.utils.MD5Util;
import com.etouch.utils.Encrypt;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;

public class CustomCredentialsMatcher implements CredentialsMatcher {
    /**
     *
     * @param token 用户输入的用户名和密码
     * @param info  数据库中的用户名和密码
     * @return  返回如果是false 则登录失败, 返回true 则登录成功
     */
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info)  {
        /**
         * 验证数据库的密码 和 用户输入的密码 是否一致
         *
         * @param token 用户输入的用户名 密码
         * @param info  数据库中的用户名 密码
         * @return 返回值 比较密码是否正确 比较正确 返回true 表示登陆成功 返回false 表示登陆失败(同时抛出异常)
         *
         * 明文密码: 不加密 原始密码
         * 密文密码: 经过一定的加密处理 , 数据更加安全
         * md5加密 : 不可逆的加密方式
         * 先将加密后的数据 以及加密前的数据 提前保存了 根据对应的数据找即可
         * 123456 ->  xxxxyyyyqqqq
         *
         * 如何屏蔽这种情况
         * 123456 ->  xxxxyyyyqqqq
         * 123456000 ->  asdasdfg
         * 用户输入的密码 + 固定值(不变的值) =  特殊的密码
         * 用户输入的密码 + 可变化的固定值 =  特殊的密码
         * password:123456 + 用户的登录名 = 特殊的密码
         * 加密: 直接转换
         * 加盐加密: 在原有的密码基础上 加上一定的参数 针对该用户而言 这个盐(新加入的参数) 是不变的
         */
       // System.out.println("密码比较器执行");
//        //加盐加密
//        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//        //获取用户输入的数据
//        String email = upToken.getUsername();
//        String password = new String(upToken.getPassword());
//        //对密码进行加密处理  加盐加密
//        String md5Pwd = Encrypt.md5(password, email);
//        //获得数据库的密码
//        String dbPwd = (String) info.getCredentials();
//        return md5Pwd.equals(dbPwd);
        //不加盐加密
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        //获得用户输入的密码
        String password = new String(upToken.getPassword());
        //加密后的密码
        String md5Pwd = MD5Util.string2MD5(password);
        //获取数据库的密码
        String dbPwd = (String) info.getCredentials();
        return md5Pwd.equals(dbPwd);
    }
}

### 6. 新建shiro的session管理器

```java

package com.etouch.config.shiro;

import com.etouch.utils.constants.Constants;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

import static org.apache.shiro.web.servlet.ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE;

/**
 *  <p> 自定义获取Token </p>
 *
 * @description :
 * @author : zhengqing
 * @date : 2019/8/23 15:55
 */
public class ShiroSessionManager extends DefaultWebSessionManager {
    /**
     * 定义常量
     */
//    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";


    /**
     * 重写构造器
     */
    public ShiroSessionManager() {
        super();
        this.setDeleteInvalidSessions(true);
    }

    /**
     * 重写方法实现从请求头获取Token便于接口统一
     * 每次请求进来,Shiro会去从请求头找REQUEST_HEADER这个key对应的Value(Token)
     */
    @Override
    public Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String token = WebUtils.toHttp(request).getHeader(Constants.REQUEST_HEADER);

        // 如果请求头中存在token 则从请求头中获取token
        if ( StringUtils.isNotBlank( token ) ) {
            request.setAttribute(REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return token;
        } else {
            // 否则按默认规则从cookie取token
            return super.getSessionId(request, response);
        }
    }
}

7. 用户名密码登陆

  /**
     * 用户名密码登录
     *
     * @param userName
     * @param passWord
     * @param rememberMe
     * @return
     */
    @ApiOperation(value = "用户名密码登录", notes = "用户名密码登录")
    @PostMapping("/login")
    public ResultUtils<String> login(HttpServletRequest request,
                                     @ApiParam(value = "用户名", required = true) @RequestParam(name = "userName") String userName,
                                     @ApiParam(value = "密码", required = true) @RequestParam(name = "passWord") String passWord,
                                     @ApiParam("记住密码,true,false") @RequestParam(name = "rememberMe") boolean rememberMe,
                                     @ApiParam("验证码") @RequestParam(name = "VerificationCode") String VerificationCode) {
        if (StringUtils.isBlank(userName) || StringUtils.isBlank(passWord)) {
            return ResultUtils.error("用户名或密码不能为空");
        }
        if (checkVerify(request, VerificationCode).getCode() != 200) {
            return ResultUtils.errorParam("验证码错误,请重新输入");
        }
        QueryWrapper<SysUser> queryWrapper = new QueryWrapper();
        queryWrapper.eq("login_name", userName);
        SysUser sysUser = sysUserService.getOne(queryWrapper);
        if (sysUser == null) {
            return ResultUtils.error("用户名不存在");
        }
        String encodePwd = MD5Util.string2MD5(passWord);
        log.info("加密码后的密码为: " + encodePwd);
        if (!encodePwd.equals(sysUser.getPassword())) {
            return ResultUtils.error("用户名或密码错误");
        }
        //登录前,先清除掉之前的用户登录信息
        this.shiroCheck();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userName, passWord, rememberMe);
        //登录操作
        Subject subject = SecurityUtils.getSubject();
        LoginUser loginUser = new LoginUser();//存入用户id和token
        String uToken = null;
        try {
            //进行shiro登录验证
            subject.login(usernamePasswordToken);
            uToken = ShiroUtils.getSession().getId().toString();
            //subject.getSession().setAttribute("loginUser", sysUser);//
            userService.login(sysUser, rememberMe, uToken);
            SecurityUtils.getSubject().getSession().setTimeout(1000*60*60*24*7);
            return ResultUtils.success("登陆成功", uToken);
        } catch (AuthenticationException e) {
            e.printStackTrace();
            return ResultUtils.error("登录失败");
        } catch (InvalidSessionException e) {
            e.printStackTrace();
            return ResultUtils.error("登录失败");
        }
    }

8 如需增加手机号, 验证码登陆, 则需进行以下配置

8.1重写shiro的token获取类
package com.etouch.config.shiro;

import org.apache.shiro.authc.HostAuthenticationToken;
import org.apache.shiro.authc.RememberMeAuthenticationToken;

import java.io.Serializable;

public class UserNamePasswordPhoneToken implements HostAuthenticationToken, RememberMeAuthenticationToken, Serializable {

    // 手机号码
    private String phone;
    private boolean rememberMe;
    private String host;

    /**
     * 重写getPrincipal方法
     */
    public Object getPrincipal() {
        return phone;
    }

    /**
     * 重写getCredentials方法
     */
    public Object getCredentials() {
        return phone;
    }

    public UserNamePasswordPhoneToken() {
        this.rememberMe = false;
    }

    public UserNamePasswordPhoneToken(String phone) {
        this(phone, false, null);
    }

    public UserNamePasswordPhoneToken(String phone, boolean rememberMe) {
        this(phone, rememberMe, null);
    }

    public UserNamePasswordPhoneToken(String phone, boolean rememberMe, String host) {
        this.phone = phone;
        this.rememberMe = rememberMe;
        this.host = host;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String getHost() {
        return host;
    }

    @Override
    public boolean isRememberMe() {
        return rememberMe;
    }
}


8.2 新建PhoneRealm 继承AuthorizingRealm
package com.etouch.config.shiro;

import com.etouch.DTO.MenuDTO;
import com.etouch.DTO.RoleDTO;
import com.etouch.DTO.UserDTO;
import com.etouch.entity.SysUser;
import com.etouch.DTO.search.SearchUserArgs;
import com.etouch.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

public class PhoneRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    // 登录认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UserNamePasswordPhoneToken token = null;
        // 如果是PhoneToken,则强转,获取phone;否则不处理。
        if (authenticationToken instanceof UserNamePasswordPhoneToken) {
            token = (UserNamePasswordPhoneToken) authenticationToken;
        } else {
            return null;
        }

        String phone = (String) token.getPrincipal();
        SearchUserArgs searchUserArgs = new SearchUserArgs();
        if (phone.contains("@")) {
            searchUserArgs.setEmail(phone);
        } else {
            searchUserArgs.setPhone(phone);
        }
        SysUser user = userService.findByArgs(searchUserArgs);
        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(user,userDTO);
        if (userDTO == null) {
            throw null;
        }
        return new SimpleAuthenticationInfo(userDTO, phone, this.getName());
    }

    /**
     * 权限校验
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserDTO user = (UserDTO) SecurityUtils.getSubject().getPrincipal();
        UserDTO userDTO = userService.findUserByName(user.getLoginName());
        for (RoleDTO role : userDTO.getRoleList()) {
            authorizationInfo.addRole(role.getRoleName());
            for (MenuDTO menu : role.getMenuDTOList()) {
                authorizationInfo.addStringPermission(menu.getMenuUrl());
            }
        }
        return authorizationInfo;
    }
    @Override
    public boolean supports(AuthenticationToken var1) {
        return var1 instanceof UserNamePasswordPhoneToken;
    }


}


8.3 新增手机号验证码登陆
 @ApiOperation("手机号,验证码登录")
    @PostMapping("/loginByPhone")
    public ResultUtils<String> login(@ApiParam("手机号") @RequestParam(name = "phone") String phone,
                                     @ApiParam("验证码") @RequestParam(name = "code") String code, @ApiParam("记住密码") Boolean rememberMe) {
        //首先校验验证码
        Boolean b = userService.checkoutCode(phone, code);
        if (!b) {
            return ResultUtils.errorParam("验证码错误,请重新输入");
        }

        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        //通过手机号或者与邮箱账号查询用户
        SearchUserArgs searchUserArgs = new SearchUserArgs();
        if (phone.contains("@")) {
            searchUserArgs.setEmail(phone);
        } else {
            searchUserArgs.setPhone(phone);
        }
        SysUser sysUser = userService.findByArgs(searchUserArgs);
        if (sysUser == null) {
            return ResultUtils.errorParam("查找不到用户信息");
        }
        this.shiroCheck();//清除掉这台客户端的上个用户的登录信息
        UserNamePasswordPhoneToken userNamePasswordPhoneToken = new UserNamePasswordPhoneToken(phone);
        //登录操作
        Subject subject = SecurityUtils.getSubject();
        LoginUser loginUser = new LoginUser();//存入用户id和token
        String uToken = null;
        try {
            //进行shiro登录验证
            uToken = ShiroUtils.getSession().getId().toString();
            subject.login(userNamePasswordPhoneToken);
            userService.login(sysUser, rememberMe, uToken);
            return ResultUtils.success("登陆成功", uToken);
        } catch (AuthenticationException e) {
            e.printStackTrace();
            return ResultUtils.error("登录失败");
        } catch (InvalidSessionException e) {
            e.printStackTrace();
            return ResultUtils.error("登录失败");
        }
    }

ShiroUtils工具类

package com.etouch.utils;

import com.etouch.DTO.UserDTO;
import com.etouch.corecenter.enums.ExceptionEnum;
import com.etouch.corecenter.exception.ProjectExeption;
import com.etouch.entity.SysUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.LogoutAware;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisSessionDAO;

import java.util.Collection;
import java.util.Objects;

/**
 * <p> Shiro工具类 </p>
 */
public class ShiroUtils {

    /**
     * 私有构造器
     **/
    private ShiroUtils() {
    }

    private static RedisSessionDAO redisSessionDAO = SpringUtil.getBean(RedisSessionDAO.class);

//    public static final String LOGIN_USER_IN_SESSION = "loginUser";

    /**
     * 传入一个用户放到session中
     * @param user
     */
//    public static void setUserInfo(User user){
//        Session session = getSession();
//        // 将用户信息放到session中
//        session.setAttribute(LOGIN_USER_IN_SESSION, user);
//    }

    /**
     * 从session中获取一个用户的信息
     */
//    public static User getUser(){
//        Session session = getSession();
//        return (User) session.getAttribute(LOGIN_USER_IN_SESSION);
//    }

    // -------------------------------------------------------------

    /**
     * 获取当前用户Session
     *
     * @Return SysUserEntity 用户信息
     */
    public static Session getSession() {
        return SecurityUtils.getSubject().getSession();
    }

    /**
     * 用户登出
     */
    public static void logout() {
        SecurityUtils.getSubject().logout();
    }

    /**
     * 获取当前用户信息
     *
     * @Return SysUserEntity 用户信息
     */
    public static UserDTO getUserInfo() {
        return (UserDTO) SecurityUtils.getSubject().getPrincipal();
    }

    /**
     * 获取当前登陆用户id
     *
     * @Return
     */
    public static String getLoginUserId() {
        UserDTO userDTO = (UserDTO) SecurityUtils.getSubject().getPrincipal();
        if (userDTO == null) {
            throw new ProjectExeption(ExceptionEnum.UNLOGIN);
        }
        return userDTO.getId();
    }

    /**
     * 删除用户缓存信息
     *
     * @Param username  用户名称
     * @Param isRemoveSession 是否删除Session,删除后用户需重新登录
     */
    public static void deleteCache(String username, boolean isRemoveSession) {
        //从缓存中获取Session
        Session session = null;
        // 获取当前已登录的用户session列表
        Collection<Session> sessions = redisSessionDAO.getActiveSessions();
        UserDTO sysUserEntity;
        Object attribute = null;
        // 遍历Session,找到该用户名称对应的Session
        for (Session sessionInfo : sessions) {
            attribute = sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (attribute == null) {
                continue;
            }
            sysUserEntity = (UserDTO) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
            if (sysUserEntity == null) {
                continue;
            }
            if (Objects.equals(sysUserEntity.getLoginName(), username)) {
                session = sessionInfo;
                // 清除该用户以前登录时保存的session,强制退出  -> 单用户登录处理
                if (isRemoveSession) {
                    redisSessionDAO.delete(session);
                }
            }
        }

        if (session == null || attribute == null) {
            return;
        }
        //删除session
        if (isRemoveSession) {
            redisSessionDAO.delete(session);
        }
        //删除Cache,再访问受限接口时会重新授权
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
        Authenticator authc = securityManager.getAuthenticator();
        ((LogoutAware) authc).onLogout((SimplePrincipalCollection) attribute);
    }

    /**
     * 从缓存中获取指定用户名的Session
     *
     * @param username
     */
    private static Session getSessionByUsername(String username) {
        // 获取当前已登录的用户session列表
        Collection<Session> sessions = redisSessionDAO.getActiveSessions();
        SysUser user;
        Object attribute;
        // 遍历Session,找到该用户名称对应的Session
        for (Session session : sessions) {
            attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (attribute == null) {
                continue;
            }
            user = (SysUser) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
            if (user == null) {
                continue;
            }
            if (Objects.equals(user.getLoginName(), username)) {
                return session;
            }
        }
        return null;
    }

}

}

java+springboot权限的设计(用户、角色、权限)和前端如何渲染用户所对应的权限菜单 记得当时在学校的时候,觉得这个实现起来真的超级困难,想想就头大,毫无头绪,即便那时候去查资料看了很多大佬写的文章,看的时候感觉恍然大悟,直拍大腿,但是当我想要动手自己去做的时候,又不知道从哪开始切入,于是一直没有动手去做,直到最近在实习的时候,给了我这个任务,当我带着恐惧去自己动手实现这个功能的时候,当静下心来,发现是多么的有趣和清晰,那我们一起来看一下吧。下面是部分代码,可能写的不是很好,因为是手搓出来的,之前刚理清一点点思路写的哈哈,仅供参考仅供参考! 阅读详情

相关推荐

以太网物理层协议整理(2)-万兆以太网

在万兆以太网标准化过程中,IEEE和10GEA(万兆以太网联盟)是两个最重要的组织。 万兆以太网标准和规范都比较繁多,在标准方面,首先有2002年的IEEE 802.3ae,后来也不断有新的万兆以太网规范推出,如2006年的IEEE 802.3an标准(10GBase-T)就推出了基于双绞铜线的万兆以太网规范,同样的IEEE 802.3aq标准推出了基于光纤的10GBase-LRM;2007年的

NowOrNever 3万+

shiro实现手机验证码登录(涉及到:自定义token、多realm配置、自定义ModularRealmAuthenticator)

shiro框架提供了一个UsernamePasswordToken令牌,用来验证用户名和密码类的登录。那如果想要通过替他方式登录认证,例如通过手机验证码接口,就需要通过自定义token、自定义realm等来实现。 1、首先,自定义一个token继承UsernamePasswordToken,为什么要继承这个类而不是AuthenticationToken?,是因为这样做保证了用户名密码认证方式任然

MODjie的博客 1万+

python数据处理与分析案例,python如何进行数据处理

大家好,本文将围绕基于python的数据处理案例展开说明,python数据处理与分析案例是一个很多人都想弄明白的事情,想搞清楚python如何进行数据处理需要先了解以下几个事情。大家好,小编来为大家解答以下问题,python数据处理案例步骤及其代码,python数据处理与分析教程,今天让我们一起来看看吧!哈喽~今天学习记录的是数据分析实训小案例。就用这个案例来好好巩固一下 python 数据分析三剑客。

a17348的博客 1237

springboot 使用shiro集成阿里云短信验证码

基于springboot,使用shiro多个realm的方法,集成阿里云的sms短息验证码服务,使用手机的短信验证码验证

tang_seven的博客 1721

Rancher操作手册(v2.7.5-rc1)

选择需要创建ingress的命名空间 ==》填写ingress名称 ==》在“规则”一栏,选择“Prefix” ==》输入访问路径 ==》选择访问路径对应的service ==》如果需要继续添加访问路径,点击“添加路径”,重复前面的步骤即可。选择需要创建Deployment的命名空间 ==》填写Deployment名称 ==》选择通用 ==》填写容器名称 ==》填写镜像 ==》选择镜像的拉取密文 ==》点击添加端口或Service。点击左侧菜单栏的集群里的“项目/命名空间”,点击右下角的“创建命名空间”。

weixin_60197334的博客 5568

毫米波雷达dd-mimo中发射天线辐射方向图变化

本文详细介绍了dd-mimo的相位调制原理,以及分离天线的办法,最后演示了在dd-mimo中因为相位调制带来的辐射方向图的变化。

weixin_44555637的博客 1167

springboot+shiro+jwt实现多端用户登录

shiro中是通过realm进行权限认证的,本身shiro是支持多realm进行权限认证的。 现有场景如下。 平台分管理角色和互联网角色,分别存储在sys_user和student表中。sys_user当然是通过管理后台来进行创建。student表中的用户是允许前端注册的,不走后台权限角色体系。 但是提供的接口均需要实现token的安全验证,毕竟不能裸奔不是。所以需要在基础框架基础上,实现多realm认证,并且使用相同token。主要是不想拆分两个后台api项目。 首先项目pom中需要导入相应的包。

哈利路亚的收藏夹 3641

强化学习:从经典强化学习理解强化学习

1. 背景介绍 强化学习(Reinforcement Learning,RL)作为机器学习领域的重要分支,近年来取得了长足的进步。其核心思想是让智能体通过与环境的交互学习最优策略,从而最大化累积奖励。不同于监督学习和非监督学习,强化学习无需预先标注数据,而是通过试错的方式逐步学习,具有更强的适应性和泛化能力。

AI架构师小马 1798

springboot+shiro/SpringSecurity登陆验证

springboot+shiro登陆验证

m0_47637225的博客 1186

30天入门 SAP BTP 开发系列之八:动手在 SAP BTP 上开发第一个 ABAP 透明表

ABAP Cloud 环境下创建透明表的方法与数据迁移实践 本文介绍了在 SAP BTP ABAP 环境中创建透明表并迁移数据的完整流程。与传统的 SE11 事务码不同,ABAP Cloud 需通过 ABAP Development Tool 创建数据库表,使用代码定义表结构。作者详细演示了如何: 在开发包中创建名为 Z优快云_TRAVEL 的透明表 使用 ABAP 代码定义表字段和约束 通过 ABAP 类实现数据从标准表/DMO/TRAVEL 到新建表的迁移 验证数据迁移结果 文中还提供了相关技术概念的

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1779

【SpringBoot 3.x 第56节】再见 Feign?原生 HTTP 接口强势上位,这才是微服务调用的正确姿势!

🏆本文收录于《滚雪球学SpringBoot 3》,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。    本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】,你想学习的都被收集在内,快速投入学习!!两不误。    若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》,涵盖SpringBoot所有版本教学文章

**My Coding Family** 1016

【文末有完整项目代码】Springboot整合shiro和JWT

前言shiro 框架是如今 web 开发做权限控制的重要选择之一,除了这个之外选择 spring security 的也很多。两者都有各自的优点。shiro 更加简单,而 spring s...

qq_18244417的博客 625

【HALCON工业视觉应用探索】08. C#+Halcon视觉引导机器人抓取:从入门到工业级实战(附完整代码+部署指南)

摘要:本文聚焦C#+Halcon在视觉引导机器人抓取领域的落地应用,从新手友好的“二十分钟基础引导系统”切入,逐步深入工业级场景(汽车变速箱壳体抓取)。内容涵盖核心概念(手眼标定原理、坐标转换、机器人通信协议)、完整技术流程(图像采集→目标定位→标定转换→机器人执行→结果反馈)、工业级代码实现(多目标识别、智能抓取策略、实时状态监控),以及现场部署关键技巧(硬件选型、通信配置、精度优化)。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 1263

fast-security-starter 简单快速集成认证授权

fast-security-starter 1.0 目标 基于jwt认证机制,角色匹配url规则,实现认证,鉴权的快速集成 介绍 对于认证,鉴权,shiro与spring-security较为成熟,但是学习成本较高。 所以想做一个可以快速集成spring-security的拓展包...

chenmiwei1262的博客 408

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

spring boot+shiro实现多种登录方式

多种登录方式说白了就是账号密码登录和第三方登录(免密码)。账号密码登录的账号可能是手机号、邮箱、身份证号等等,这些到了shiro处理的时候就是username+password的处理;而第三方登录在本系统只需要校验用户是否存在。 所涉及的类 开始 引入shiro maven <dependency> <groupId>org.apache.shiro<...

qq_37754001的博客 4945

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Shiro使用Redis作为缓存(解决Shiro频繁访问Redis)(十一)

原文:https://blog.youkuaiyun.com/qq_34021712/article/details/80791219 之前写过一篇博客,使用的一个开源项目,实现了redis作为缓存 缓存用户的权限 和 session信息,还有两个功能没有修改,一个是用户并发登录限制,一个是用户密码错误次数.本篇中几个类 也是使用的开源项目中的类,只不过是拿出来了,redis单独做的配置,方便进行优化。 整合过程 1.首先是整合Redis Redis客户端使用的是RedisTemplate,自己写了一个序列化工具继承R

Doge的技术小屋 1920

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料

由于公司项目的需要,要实现在项目中使用第三方授权登录以及分享文字和图片等这样的效果,几经波折,查阅了一番资料,做了一个Demo。实现起来的效果还是不错的,不敢独享,决定写一个总结的教程,供大家互相交流、学习和参考,博主只求能和大家共同进步。希望能多多支持!         这篇文章中,我们使用到了Share SDK,它是为iOS、Android、WP8的APP提供社会化功能的一个组件,目前支持如

我是一条寻水的鱼 1万+

SpringSecurity实现角色权限控制(SpringBoot+SpringSecurity+JWT)

通过springboot整合jwt和security,以用户名/密码的方式进行认证和授权。认证通过jwt+数据库的,授权这里使用了两种方式,分别是SpringSecurity自带的hasRole方法+SecurityConfig和 我们自定义的permission+@PreAuthorize注解。用来存储和获取当前线程关联的 SecurityContext 对象的类。

lans_g的博客 9317

shiro登录与角色

1登录--shiro身份认证 1.1 修改数据库密码-让它经过加密处理 我们不知道数据库的密码 需要修改一下 准备一个加密算法 public class MD5Util { // String algorithmName, Object source, Object salt, int hashIterations //设置盐值 public static fina...

weixin_45527690的博客 269

shiro认证登录与角色

shiro的认证流程:(源码大概流程是主体执行登陆验证是通过SecurityManager来操作,核心通过认证器(Authenticator),认证器通过去Realm中寻找数据进行对比验证) 1、创建SecurityManager核心环境,将其设置为环境; 2、获取主体; 3、传入认证数据提交; 4、在这之前别忘记指定存放数据的Realm shiro验证角色流程与认证登录类似:(源码大概流程主体验证角色时是通过SecurityManager,核心环境通过授权器(Authorizer),授权器通过去Realm

m0_50250551的博客 376

Springboot权限管理

现在鉴权模块已经非常成熟,以上仅为个人心得和实践,还有很多改进的地方欢迎评论,后续还会补充通过Shiro框架实现的鉴权这一模块。

zkk的博客 2715

springboot 整合 Spring Security 中篇(RBAC权限控制)

主要在这个方法loadUserByUsername 从数据库读取用户的登录信息和权限信息import comthrow new UsernameNotFoundException("用户名不存在");} }throw new UsernameNotFoundException("用户名不存在");} }

qq_30519365的博客 2354

Spring Boot实现简单的用户权限管理(超详细版)

为了避免浪费时间进行不必要的阅读,这里先对项目进行简单的介绍。在实际应用场景中,每个用户都有对应的角色,而每个角色又有对应的一些角色。因为一个用户可以有多个角色,一个角色也可以被多个用户所拥有,角色和权限的关系也同理,这里主要利用多对多的映射关系将他们联系起来,对他们进行管理。 ...

Veggie的博客 7万+

Spring安全的角色和权限源码与教程

在本文中,我们将研究Spring安全角色和特权以及如何使用此功能来构建您的应用程序。 企业应用程序包含多个部分,它不允许所有用户访问整个应用程序。我们可能会提出一些要求,即我们希望根据用户角色和特权提供对应用程序的访问。让我们以管理电子商务商店的简单后端应用程序为例。 具有ADMIN角色的用户将具有执行任何操作的完全权限。 客户服务代理可以读取客户和订单信息,但看不到其他选项。 产品经理只能看到更新/创建产品的选项。 Spring安全性使使用角色和特权来构建这些类型的规则变得更加容易。我们可以在注册

m0_67645544的博客 839

springboot 实现权限管理(一)

若token失效则返回失效,token未失效进行权限查看(token及权限信息等常用个人信息采用Redis进行缓存),有权限放行,无权限拦截;(2)用户访问登录URL,则无需拦截,判断用户、密码,进行缓存个人信息(查询关系数据库的用户角色、权限信息后进行缓存),并返回token给用户作为下次登录凭证;(2)分类资源(对应相应的权限),一般可以有数据权限、操作权限、访问权限等,表现为对URL(URI)的访问控制;在关系数据库中,由于原子性(第一规范),因此需要两张关联表进行管理用户和角色,角色和权限。...

iijik55的博客 5334

SpringSecurity项目如何实现角色包含关系(角色继承)

近期在阅读 Spring Security 官方文档时发现关于 `Hierarchical Roles`的介绍

console的博客 638
上一篇: mybatis-plus自动生成实体类(包含swagger注解)
下一篇: 开发中常用的Mybatis使用总结
意田天
博客等级 码龄7年 41粉丝 89原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

意田天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值