springboot整合shiro

一、什么是shiro?
1、Shiro是一个安全框架,可以进行角色、权限管理。
Shiro主要功能如下:
Authentication(认证):用户身份识别,通常被称为用户“登录”
Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。
Web Support:Web支持,可以非常容易的集成到Web环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
(记住一点,Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。)

2、shiro主要的类
①Subject:当前用户,Subject可以是一个人,也可以是第三方服务
②SecurityManager:管理所有Subject,可以配合内部安全组件。
③principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
④credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
最常见的principals和credentials组合就是用户名/密码了。
⑤Realms:用于进行权限信息的验证,需要自己实现。
Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。
⑥在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。
我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
⑦SimpleHash,可以通过特定算法(比如md5)配合盐值salt,对密码进行多次加密。

二、接下来开始整合shiro
1、引入所需要的jar包依赖

<!-- shiro -->
        <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.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.2</version>
        </dependency>

2、创建实体类

User.java

import lombok.Data;

@Data
public class User {
    private Integer userId;

    private String userName;

    private String password;

    private String status;

}

Role.java

import lombok.Data;

@Data
public class Role {
    private Integer roleId;

    private String roleName;

    private String roleNote;
}

UserRole.java

import lombok.Data;

@Data
public class UserRole {
    private Integer userRoleId;

    private Integer userId;

    private Integer roleId;

    private String userRoleNote;
}

RolePermission.java

import lombok.Data;
@Data
public class RolePermission {
    private Integer rolePermissionId;

    private Integer roleId;

    private String permissionName;
}

3、创建shiro的realm
MyShiroRealm.java

import com.vtech.packinglist.common.GlobalConstant;
import com.vtech.packinglist.exception.UsersServiceException;
import com.vtech.packinglist.mapper.UserMapper;
import com.vtech.packinglist.pojo.User;
import lombok.extern.slf4j.Slf4j;
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.apache.shiro.subject.Subject;
import javax.annotation.Resource;
import java.util.Set;

@Slf4j
public class MyShiroRealm extends AuthorizingRealm {

    @Resource
    private UserMapper userMapper;

    /**
     * 权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        User users = (User) SecurityUtils.getSubject().getPrincipal();
        Set<String> role = userMapper.selectRoleNameByUserId(users.getUserId());
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRoles(role);
        return simpleAuthorizationInfo;
    }

    /**
     * 身份认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 将AuthenticationToken强转为UsernamePasswordToken对象
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        // 获得从表单传过来的用户名
        String username = upToken.getUsername();
        String pwd = String.valueOf(upToken.getPassword());
        Subject subject = SecurityUtils.getSubject();
        User user = null;
        try {
            User users = userMapper.findUserStatusByUserName(username);
            // 如果用户不存在,抛此异常
            if (null == users) {
                throw new UnknownAccountException("Can not find this user!");
            }
            user = userMapper.findUserByUserAndPwd(username, pwd);
            if (user == null) {
                throw new IncorrectCredentialsException();
            }
            subject.getSession().setAttribute(GlobalConstant.SESSTION_AUTH_USERINFO, SecurityUtils.getSubject().getPrincipal());
            subject.getSession().setAttribute(GlobalConstant.USER_NAME, user.getUserName());
        } catch (UsersServiceException e) {
            log.error(e.getMessage(),e);
        }
        // 创建SimpleAuthenticationInfo对象,并且把username和password等信息封装到里面
        // 用户密码的比对是Shiro帮我们完成的
        return new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
    }
}

4、创建shiro配置类
shiroConfig.java

import com.vtech.packinglist.common.LocalFromAuthenticationFilter;
import com.vtech.packinglist.realm.MyShiroRealm;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;


/**
 * Shiro 内置过滤器,可以实现权限相关的拦截器
 * 常用的过滤器:
 * anon:无需认证(登录)可以访问
 * authc:必须认证才可以访问
 * user:如果使用rememberMe的功能可以直接访问
 * perms:该资源必须得到资源权限才可以访问
 * role:该资源必须得到角色的权限才可以访问
 */

@Slf4j
@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {    
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 设置login URL
        shiroFilterFactoryBean.setLoginUrl("/login.html");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/booking/index.html");
        // 未授权的页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized.html");
        HashMap<String, Filter> hashMap = new HashMap<String, Filter>();
        hashMap.put("shiroLoginFilter", shiroLoginFilter());
        shiroFilterFactoryBean.setFilters(hashMap);
        //添加Shiro内置过滤器
        // 拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //静态资源
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/font/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/plugins/**", "anon");
        filterChainDefinitionMap.put("/scripts/**", "anon");
        // 设置登录的URL为匿名访问,因为一开始没有用户验证
        filterChainDefinitionMap.put("/login.do", "anon");
        // 退出系统的过滤器
        filterChainDefinitionMap.put("/logout.do", "logout");
        // 现在资源的角色
        //filterChainDefinitionMap.put("/hello/welcome.do", "roles[test]");
        // filterChainDefinitionMap.put("/user.html", "roles[user]");
        //其余接口一律拦截
        //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        log.info("Shiro拦截器工厂类注入成功");
        return shiroFilterFactoryBean;
    }

    /**
     * 盐
     * 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     * 所以我们需要修改下doGetAuthenticationInfo中的代码; )
     */
    /*@Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(1024);// 散列的次数,比如散列两次,相当于md5(md5(""));
        return hashedCredentialsMatcher;
    }*/

    /**
     * 自定义身份认证 realm;
     * 必须写这个类,并加上 @Bean 注解,目的是注入 MyShiroRealm,
     */
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        //需要盐时才添加(加密)
        //myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    /**
     * 注入 securityManager
     */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 注入自定义的realm;
        securityManager.setRealm(myShiroRealm());
        // 注入缓存管理器;
        //securityManager.setCacheManager(ehCacheManager());
        return securityManager;
    }

    /**
     * 开启shiro aop注解支持 使用代理方式;所以需要开启代码支持;
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    /**
     * 处理ajax请求
     */
    @Bean(name = "shiroLoginFilter")
    public LocalFromAuthenticationFilter shiroLoginFilter() {
        LocalFromAuthenticationFilter shiroLoginFilter = new LocalFromAuthenticationFilter();
        return shiroLoginFilter;
    }

    /**
     * shiro缓存管理器;
     * 需要注入对应的其它的实体类中-->安全管理器:securityManager可见securityManager是整个shiro的核心;
     */
    /* @Bean
     public EhCacheManager ehCacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return cacheManager;
    }*/

}

5、创建Mapper
UserMapper.java

public interface UserMapper {

    User findUserByUserAndPwd(@Param("userName") String userName, @Param("password") String password);

    User findUserStatusByUserName(String userName);

    Set<String> selectRoleNameByUserId(Integer userId);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.vtech.packinglist.mapper.UserMapper" >
  <resultMap id="BaseResultMap" type="com.vtech.packinglist.pojo.User" >
    <id column="USER_ID" property="userId" jdbcType="INTEGER" />
    <result column="USER_NAME" property="userName" jdbcType="NVARCHAR" />
    <result column="PASSWORD" property="password" jdbcType="NVARCHAR" />
    <result column="STATUS" property="status" jdbcType="NVARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    USER_ID, USER_NAME, PASSWORD, STATUS
  </sql>
 
  <select id="findUserByUserAndPwd" resultMap="BaseResultMap">
    select * from TS_USER  where user_name = #{userName} and password= #{password} and status='ACTIVE'
  </select>
  <select id="findUserStatusByUserName" resultMap="BaseResultMap">
    select *
        from TS_USER
        where 1=1 and user_name = #{userName}
        and status = 'ACTIVE'
  </select>
  <select id="selectRoleNameByUserId" resultType="java.lang.String">
    select USER_ROLES.ROLE_NAME from TS_USER
        left join  USER_ROLES on users.USER_ID = USER_ROLES.USER_ID
        where  USERS.USER_ID = #{userId}
  </select>
</mapper>

6、创建LoginController.java
LoginController.java

   @RestController
@Slf4j
public class LoginController {

    @ApiOperation(value="登录界面", notes="")
    @RequestMapping(value = "/login.html",method = RequestMethod.GET)
    public ModelAndView test() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("common/login");
        return modelAndView;
    }

    @ApiOperation(value="404 not found页面", notes="")
    @RequestMapping(value = "/404.html",method = RequestMethod.GET)
    public ModelAndView page404() {
        return new ModelAndView("/hui/404");
    }

    @ApiOperation(value="没有权限页面", notes="")
    @RequestMapping(value = "/unauthorized.html",method = RequestMethod.GET)
    public ModelAndView unauthorized(){
        return new ModelAndView("/hui/_blank");
    }

    @ApiOperation(value = "系统主页", notes = "")
    @RequestMapping(value = "/index",method = RequestMethod.GET)
    public ModelAndView index(){
        return new ModelAndView("/common/index");
    }

    @RequestMapping(value = "/home.html",method = RequestMethod.GET)
    public ModelAndView home(){
        return new ModelAndView("/common/home");
    }

    @LogAnno("请求登录")
    @ApiOperation(value="请求登录", notes="登录操作")
    @RequestMapping(value = "/login.do",method = RequestMethod.POST)
    public JsonResult login(String username, String password) {
        //判断账号和密码是否为空,若为空则返回提示信息
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            return new JsonResult(JsonResult.ERROR, "Please enter your account name or password!");
        }
        try {
            // 从SecurityUtils里边创建一个 subject
            Subject subject = SecurityUtils.getSubject();
            // 在认证提交前准备 token(令牌)
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            // 执行认证登陆
            subject.login(token);
        } catch (IncorrectCredentialsException e) {
            return new JsonResult(JsonResult.ERROR, "The account or password is wrong!");
        } catch (LockedAccountException e) {
            return new JsonResult(JsonResult.ERROR, "Account is disable or does not exist!");
        } catch (ConcurrentAccessException e) {
            return new JsonResult(JsonResult.ERROR, "Login failed,please log in again!");
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResult(JsonResult.ERROR, e.getMessage());
        }
        return new JsonResult(GlobalConstant.AUTHENTICATION_SUCCESS);
    }

    @ApiOperation(value="登出", notes="")
    @LogAnno("退出登录")
    @RequestMapping(value = "/logout.do",method = RequestMethod.GET)
    public ModelAndView logout() {
        return new ModelAndView("hui/login");
    }
}

到这里就完成了整合shiro实现登录的功能,在shiroConig中设置拦截的url即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程简介:历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值