Springboot整合Shiro实现登录加密和权限

·  阅读 650

这是我参与更文挑战的第23天,活动详情查看: 更文挑战 使用工具:IDEA,MySQL 数据库表:teacher(用户),role(角色),perms(权限)

teacher表

image1.jpeg

role表 image2.png

perms表 image3.png

image4.png

shiro是一个安全框架,可以进行认证、授权、密码加密、会话管理 从外部来解析shiro框架:

image5.png

Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;

SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;

Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

使用:

引入依赖

</dependency>
<!--Shiro整合-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
复制代码

其他依赖

<!--thymeleaf依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    <version>2.1.1.RELEASE</version>
<!--thymeleaf页面使用shiro标签-->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>
<!--shiro集成ehcache缓存-->

复制代码

配置类ShiroConfig和自定义Realm

ShiroConfig初始3个bean

//创建ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    //设置安全管理器
    shiroFilterFactoryBean.setSecurityManager(securityManager);

    //添加Shiro内置过滤器
    /*Shiro内置过滤器,实现权限相关的拦截器
    *常用过滤器:
    *   anon:无需认证可以访问
    *   authc:必须认证才可以访问
    *   user:如果使用rememberMe功能可以直接访问
    *   perms:该资源必须得到资源权限才可以访问
    *   role:给资源必须得到角色权限才可以访问
    *
    * */
    Map<String,String> filterMap = new LinkedHashMap<>();
    filterMap.put("/test","anon");
    filterMap.put("/login","anon");
    filterMap.put("/doRegister","anon");
    filterMap.put("/register","anon");
    //授权过滤器
    //当前授权拦截和,shiro自动跳转到未授权页面
    filterMap.put("/add","perms[user:add]");
    filterMap.put("/update","perms[user:update]");
    filterMap.put("/*","authc");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
    //默认跳转到login.jsp,修改为toLogin
    shiroFilterFactoryBean.setLoginUrl("/toLogin");
    //设置未授权页面
    shiroFilterFactoryBean.setUnauthorizedUrl("/unAuth");
    return shiroFilterFactoryBean;
}
复制代码
//创建DefaultWebSecurityManager
@Bean("securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    //关联Realm
    securityManager.setRealm(userRealm);
    //
    securityManager.setSessionManager( getDefaultWebSessionManager() );
    return securityManager;
}
//创建Realm
@Bean("userRealm")
public UserRealm getRealm(){
    return new UserRealm();
}
复制代码

自定义Realm UserRealm.java

继承: extends AuthorizingRealm
重写两个方法

//执行授权逻辑
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    System.out.println("执行授权");
    return null;
}
//执行认证逻辑
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    System.out.println("执行认证"); 
}
复制代码

Ctrl层

Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(name,password);
复制代码

登录
subject.login(token);

登出
subject.logout();

权限管理
认证时获取角色,权限

List<Role> rlist = rMapper.roleList(login.getRid());//获取用户角色
List<Perms> plist = pMapper.permList(login.getRid());//获取用户权限
List<String> roleStrlist=new ArrayList<>();////用户的角色集合
List<String> perminsStrlist=new ArrayList<>();//用户的权限集合
for (Role role : rlist) {
    roleStrlist.add(role.getRole_name());
}
for (Perms uPermission : plist) {
    perminsStrlist.add(uPermission.getPerm());
}
login.setRoleStrlist(roleStrlist);
login.setPerminsStrlist(perminsStrlist);
授权时添加权限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//用户的角色集合
info.addRoles(teacher.getRoleStrlist());
//用户的权限集合
info.addStringPermissions(teacher.getPerminsStrlist());

密码加密
Shiroconfig类中添加两个bean  ,定义加密方法,注入
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
    HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
    hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法;
    hashedCredentialsMatcher.setHashIterations(1024);// 散列的次数,比如散列两次,相当于md5(md5(""));
    hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);//是否16进制
    return hashedCredentialsMatcher;
}
/**
 * 身份认证 realm
 */
@Bean("myShiroRealm")
public UserRealm myShiroRealm(){
    UserRealm myShiroRealm = new UserRealm();
    myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
    System.out.println("myShiroRealm 注入成功");
    return myShiroRealm;
}
Realm认证时
SimpleAuthenticationInfo info = null;
     String realname = getName();
    // 盐值加密,可以用用户名
    ByteSource salt = ByteSource.Util.bytes(tname);
    info = new SimpleAuthenticationInfo(login,login.getPassword(),salt, realname);
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("USER_SESSION", login);
盐:增加加密的复杂度,增加hash
复制代码

Shiro中MD5加密方法

ByteSource credentialsSalt01 = ByteSource.Util.bytes("张三");
Object credential = "123456";//密码
String hashAlgorithmName = "MD5";//加密方式
//1024指的是加密的次数
Object simpleHash = new SimpleHash(hashAlgorithmName, credential,
        credentialsSalt01, 1024);
System.out.println("加密后的值----->" + simpleHash)
复制代码

缓存机制

为什么要用缓存呢?在页面上访问一些需要角色或权限才能访问的url,你会发现每次访问都去数据库拿对应用户拥有的角色权限,试想一下,我每访问一个url,都要去数据库重复拿这些数据,显然是一个不成熟的做法,如果有了缓存,我们只需要查询一次数据库,之后访问url都将从缓存中拿数据而不是数据库,这样数据库压力明显降低,看起来也相对成熟。 添加依赖

Shiroconfig添加bean
@Bean
public EhCacheManager getCacheManager(){
    EhCacheManager ehCacheManager = new EhCacheManager();
    //ehCacheManager.setCacheManagerConfigFile("src\\main\\resources\\shiro-ehcache.xml");
    return ehCacheManager;
}
添加到securityManager
securityManager.setCacheManager(getCacheManager());

重复登录
Shiroconfig配置
//禁止重复登录
// 配置sessionDAO
@Bean(name="sessionDAO")
public MemorySessionDAO getMemorySessionDAO(){
    MemorySessionDAO sessionDAO = new MemorySessionDAO();
    return sessionDAO;
}

//配置shiro session 的一个管理器
@Bean(name = "sessionManager")
public DefaultWebSessionManager getDefaultWebSessionManager(){
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    // 设置session过期时间
    sessionManager.setGlobalSessionTimeout(60*60*1000);
    // 请注意看代码
    sessionManager.setSessionDAO(getMemorySessionDAO());
    //解决url携带jessionid问题
    sessionManager.setSessionIdUrlRewritingEnabled(false);
    return sessionManager;
}

Realm认证时判断删除session
//禁止同时登陆
if( tname.equals( login.getName() ) && md5.equals( login.getPassword() ) ){
    // 获取所有session
    Collection<Session> sessions = sessionDAO.getActiveSessions();
    for (Session session: sessions) {
        Teacher sysUser = (Teacher) session.getAttribute("USER_SESSION");
        // 如果session里面有当前登陆的,则证明是重复登陆的,则将其剔除
        System.out.println("seesion:"+session.getId());
        if( sysUser!=null ){
            if( tname.equals( sysUser.getName() ) ){
                session.setTimeout(0);
            }
        }
    }
}

复制代码

其他

//配置ShiroDialect,用于thymeleaf和shiro标签配合使用
@Bean
public ShiroDialect getShiroDialect(){
    return new ShiroDialect();
}
复制代码
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改