在一个系统中,不同的用户会对应着不同的角色,不同的角色会被赋予不同的权限。不同角色的用户登陆会看到不同的界面,也就是意味着可以使用不同的功能。可以使用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 "登录页";//对应着登录页面的页面名称
}
}
记录下来,作为笔记,还有很多地方理解的不透彻,不深入,后续有时间有更多的理解会添加上来。