在一个系统中,不同的用户会对应着不同的角色,不同的角色会被赋予不同的权限。不同角色的用户登陆会看到不同的界面,也就是意味着可以使用不同的功能。可以使用Spring security或者Shiro,但是Shiro相对于Spring security来说更轻量一些,更容易上手,因此选择了后者。

首先,在Springboot工程的pom文件里添加shiro依赖,如下:

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

接下来,对Shiro进行配置,使用注解@Configuration,主要代码如下:

@Configuration
public class ShiroConfiguration {
    
    @Bean
    public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager){
        
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        
        //设置SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        
        
        //拦截器
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        
        //所有匿名用户均可访问到Controller层的该方法下
        filterChainDefinitionMap.put("/login","anon");
        filterChainDefinitionMap.put("/static/**","anon");
        filterChainDefinitionMap.put("/loginInfo", "anon");
        //filterChainDefinitionMap.put("/Welcome", "anon");
        //配置退出过滤器
        filterChainDefinitionMap.put("/login/logout", "logout");
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/**", "authc");//经过认证才可以访问
        

        
        //如果不设置默认会自动寻找web工程根目录下的/login.jsp  当前就不配置了  直接去找login.jsp
        shiroFilterFactoryBean.setLoginUrl("/login");//配置后  输入/index会去访问login界面
        
        //登陆后要跳转的链接
        //shiroFilterFactoryBean.setSuccessUrl("/Welcome");//如果在jsp中实现了成功登陆后的跳转  是不是这块也可以不进行配置
        
        //未授权页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/login");
        //自定义拦截器
        Map<String,Filter> filterMap = new LinkedHashMap<String,Filter>();
        
        //
        shiroFilterFactoryBean.setFilters(filterMap);
        
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    
    @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        
        securityManager.setRememberMeManager(rememberMeManager());
        //一开始被我注掉了 导致rememberMe设置的时候cookie有效时长一致不生效
        return securityManager;
    }
    
    @Bean
    public MyShiroRealm myShiroRealm(){
        return new MyShiroRealm();
    }
    
    //cookie对象
    @Bean
    public SimpleCookie rememberMeCookie(){
        
        System.out.println("rememberMeCookie");
        //这个参数是cookie的名称,对应前端的checkbox的name=rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //记住我cookie生效时间1天 s
        simpleCookie.setMaxAge(10800);//有效时长当前设置为3个小时 和Session有效时长一致
        return simpleCookie;
    }
    
    @Bean
    //cookie管理对象 记住我功能
    public CookieRememberMeManager  rememberMeManager(){
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //String key = "3AvVhmFLUs0KTA3Kprsdag==";
        return cookieRememberMeManager;
    }

}

其中,MyShiroRealm类中实现登录的认证和用户授权。使MyShiroRealm类继承AuthorizingRealm类,需要实现protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)和protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)两个方法,其中doGetAuthorizationInfo方法主要实现授权用户权限(用户角色和权限控制),doGetAuthenticationInfo方法获取用户身份,进行身份验证。(以上两个方法暂时没有详细阐述),在doGetAuthenticationInfo()方法中UnknownAccountException()对应着账户不存在,AuthenticationException()可以对应着密码错误。<简单判定>

上面的配置信息中,rememberMeCookie可实现在用户登录过程中,选择“记住我”等相关功能时(且已设置cookie有效时长,表示不再是会话cookie),在浏览器端,也就是本地将cookie缓存下来。下次用户登录时,会携带cookie请求服务器,若cookie和session均在有效时长内(即判断用户为已登录状态),会直接访问欢迎页。

用户输入用户名和密码等相关信息后,访问的接口代码如下:

@CrossOrigin
	@RequestMapping("/loginInfo")
	@ResponseBody
	public JSONObject Login(@RequestParam("username")String username,
			@RequestParam("password")String password
			,HttpServletRequest request,HttpServletResponse response) throws AuthenticationException{
		response.setHeader("Access-Control-Allow-Origin","*");
		response.setHeader("Cache-Control","no-cache");
		response.setHeader("Content-Type", "text/html; charset=UTF-8");  
		response.setContentType("text/html; charset=UTF-8");
		boolean rememberMeFlag = false;
		String rememberMe = request.getParameter("rememberMe");
		//如果用户没有选择"记住我"返回的是null,如果选择了返回的是"on"
		if(null == rememberMe){
			rememberMeFlag = false;
		}else{
			rememberMeFlag = true;
		}
		System.out.println("登录");
		
				
		Map<String,Object> resultMap = new HashMap<String,Object>();
		//Shiro认证
		Subject subject = SecurityUtils.getSubject();
		//收集用户的用户名和密码
		UsernamePasswordToken token = new UsernamePasswordToken(username,password);
			try{
				token.setRememberMe(rememberMeFlag);
				subject.login(token);//进行验证
			}catch(UnknownAccountException e){
				resultMap.put("code", "code1");
				resultMap.put("msg", "账户不存在");
				return new JSONObject(resultMap);
			}catch(DisabledAccountException e){
				resultMap.put("code", "code2");
				resultMap.put("msg", "账户存在问题");
				return new JSONObject(resultMap);
			}catch(Exception e){
				resultMap.put("code", "code3");
				resultMap.put("msg", "登陆异常");
				return new JSONObject(resultMap);
			}
			
			resultMap.put("code", "code4");
			resultMap.put("msg", "登陆成功");
			
			Session session = SecurityUtils.getSubject().getSession();
			
			//验证成功后将用户名存放到session中
			session.setAttribute("****", username);

			
			session.setTimeout(10800000);//当前设置session为三个小时  和cookie有效时长一致
		
		return new JSONObject(resultMap);

	}

上述代码中的code1、code2...等几个code码主要用于页面不同状态下的弹框提示,可协商修改(也和MyShiroRealm类中doGetAuthenticationInfo方法进行身份验证时出现的Exception对应)。当然,如果要实现用户有效时长内自动登录,还需要将做用户是否已登录的判断,选择直接判断,映射到不同的页面。

@RequestMapping("/login")
	public String Login(){
		Subject subject = SecurityUtils.getSubject();
		if(subject.isAuthenticated()){//用户已经登录
			return "欢迎页";//对应着欢迎页的页面名称
		}else{
			return "登录页";//对应着登录页面的页面名称
		}	
	}

记录下来,作为笔记,还有很多地方理解的不透彻,不深入,后续有时间有更多的理解会添加上来。