【Shiro】Springboot整合Shiro实现登录和注册

简单了解Shiro

基本概念

  • Subject:主体,代表当前‘用户’ 。这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委派给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者
  • Shiro SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互且它管理者所有Subject;可以看出它是Shiro的核心,它负责与后面介绍的其它组件进行交互,可以把它看成DispathcherServlet前端控制器
  • Realm: 域,Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

内部结构框架

  • Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”
  • SecurityManager : 相 当 于 SpringMVC 中 的 DispatcherServlet 或 者 Struts2 中的
  • FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。
  • Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
  • Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
  • Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;
  • SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所有呢,Shiro 就抽象了一个自己的 Session来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,
    这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);
  • SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO中可以使用 Cache 进行缓存,以提高性能;
  • CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
  • Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密

在这里插入图片描述

JPARealm:通过 JPA 进行验证的 Realm

(1)Realm概念
(2)继承AuthorizingRealm
(3)重写授权方法doGetAuthorizationInfo
(4)重写认证方法doGetAuthenticationInfo

//自定义realm,shiro从Realm里获取安全数据
//意思就是自己写一个类继承AuthorizingRealm类,然后重写AuthorizingRealm的getAuthorizationInfo方法
public class JPARealm extends AuthorizingRealm {

	@Autowired
	private UserService userService;

	//(1)重写授权方法doGetAuthorizationInfo
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

		//SimpleAuthorizationInfo:代表用户角色权限信息
		通过名称查找权限,简化操作(正常是通过名称找角色,通过角色查询对应的权限)
		SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
		return s;
	}

	//(2)重写认证方法doGetAuthenticationInfo
	//用户登录时会调用这个方法
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// 从token里获取身份信息:用户名userName,token代表用户输入的信息
		// principal : 主体的标示,可以有多个,但是需要具有唯一性,常见的有用户名,手机号,邮箱等
		String userName = token.getPrincipal().toString();
		//根据用户名(唯一标识)从数据库里查到这个用户
		User user = userService.getByName(userName);
		//取出这个用户的密码
		String passwordInDB = user.getPassword();
		//取出这个用户的盐
		String salt = user.getSalt();
		//SimpleAuthenticationInfo :代表该用户的认证信息。参数为“用户名+密码+盐”
		//this.getName()是获取CachingRealm的名字
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, passwordInDB, ByteSource.Util.bytes(salt),
				getName());
		return authenticationInfo;
	}

}

Shiro 配置

进行 Shiro配置,其中的 bean 都是常规配置,需要关注的有两个:

  1. getJPARealm() 指定了 Realm 使用 JPARealm。
  2. hashedCredentialsMatcher() 指定了 加密算法使用 md5,并且混进行2次加密。
@Configuration
public class ShiroConfiguration {
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        return shiroFilterFactoryBean;
    }
    
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(getJPARealm());
        return securityManager;
    }

    @Bean
    public JPARealm getJPARealm(){
        JPARealm myShiroRealm = new JPARealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }

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

注册

其中注册时候的时候,会通过随机方式创建盐, 并且加密算法采用 “md5”, 除此之外还会进行 2次加密。
这个盐,如果丢失了,就无法验证密码是否正确了,所以会数据库里保存起来。

    //shiro加持的注册方法
    @PostMapping("/foreregister")
    public Object register(@RequestBody User user) {
        //先取得用户的名字,密码,这两个是画面传进来的参数,放在User里
        String name =  user.getName();
        String password = user.getPassword();
        name = HtmlUtils.htmlEscape(name);
        user.setName(name);

        //判断用户名是否已经存在,如果已存在,那就返回错误信息
        boolean exist = userService.isExist(name);
        if(exist){
            String message ="用户名已经被使用,不能使用";
            return Result.fail(message);
        }
        //SecureRandomNumberGenerator类随机方法创建盐,进行两次加密,加密算法用md5
        String salt = new SecureRandomNumberGenerator().nextBytes().toString();
        int times = 2;
        String algorithmName = "md5";

        //SimpleHash类使用md5加密算法加密两次,把盐加进去,生成新的密码
        String encodedPassword = new SimpleHash(algorithmName, password, salt, times).toString();

        //把盐和生成的加密密码,存到数据库里
        user.setSalt(salt);
        user.setPassword(encodedPassword);
        userService.add(user);

        return Result.success();
    }

登录

登陆的时候, 通过 Shiro的方式进行校验

    //shiro加持的登录方法,参数传进来一个user用户对象
    @PostMapping("/forelogin")
    public Object login(@RequestBody User userParam, HttpSession session) {
        //获取登录时输入的用户名
        String name =  userParam.getName();
        name = HtmlUtils.htmlEscape(name);

        Subject subject = SecurityUtils.getSubject();
        //把输入的用户名+密码,放在token里面
        UsernamePasswordToken token = new UsernamePasswordToken(name, userParam.getPassword());
        try {
            subject.login(token);
            User user = userService.getByName(name);
//          subject.getSession().setAttribute("user", user);
            session.setAttribute("user", user);
            return Result.success();
        } catch (AuthenticationException e) {
            String message ="账号密码错误";
            return Result.fail(message);
        }

    }

退出登录

@GetMapping("/forelogout")
public String logout( ) {
    Subject subject = SecurityUtils.getSubject();
    if(subject.isAuthenticated())
        subject.logout();
    return "redirect:home";
}

拦截器判断是否登录

拦截器里会判断是否登陆,切换为 Shiro 方式:
subject.isAuthenticated()

public class LoginInterceptor implements HandlerInterceptor {
	@Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
    	HttpSession session = httpServletRequest.getSession();
        String contextPath=session.getServletContext().getContextPath();
        String[] requireAuthPages = new String[]{
        		"buy",
        		"alipay",
        		"payed",
        		"cart",
        		"bought",
        		"confirmPay",
        		"orderConfirmed",
        		
        		"forebuyone",
        		"forebuy",
        		"foreaddCart",
        		"forecart",
        		"forechangeOrderItem",
        		"foredeleteOrderItem",
        		"forecreateOrder",
        		"forepayed",
        		"forebought",
        		"foreconfirmPay",
        		"foreorderConfirmed",
        		"foredeleteOrder",
        		"forereview",
        		"foredoreview"
        		
        };
 
        
        String uri = httpServletRequest.getRequestURI();

        uri = StringUtils.remove(uri, contextPath+"/");
        String page = uri;

        
        if(begingWith(page, requireAuthPages)){
			/*User user = (User) session.getAttribute("user");
    		if(user==null) {
            	httpServletResponse.sendRedirect("login");
                return false;
            }*/
			Subject subject = SecurityUtils.getSubject();
			if(!subject.isAuthenticated()) {
				httpServletResponse.sendRedirect("login");
				return false;
			}
        }
        return true;   
    }

    private boolean begingWith(String page, String[] requiredAuthPages) {
    	boolean result = false;
    	for (String requiredAuthPage : requiredAuthPages) {
			if(StringUtils.startsWith(page, requiredAuthPage)) {
				result = true;	
				break;
			}
		}
    	return result;
	}

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    }
}

前台判断是否登录

    @GetMapping("forecheckLogin")
    public Object checkLogin() {
        Subject subject = SecurityUtils.getSubject();
        if(subject.isAuthenticated())
            return Result.success();
        else
            return Result.fail("未登录");
    }
  • 6
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值