Shiro与Sping Security均是java的安全框架,主要用于处理用户身份验证和授权。
验证与授权
常见场景为用户系统登录
shiro
Shiro易用性强,提供了认证,授权,加密,和会话管理功能。
Shiro的三大核心组件 :
Subject:即当前用户概念,不止代表着某用户,也可以是进程或任何可能的事物。
SecurityManager:即所有Subject的管理者,可以把他看做是一个Shiro框架的全局管理组件,用于调度各种Shiro框架的服务。作用类似于SpringMVC中的DispatcherServlet,用于拦截所有请求并进行处理。
Realm:Realm是用户的信息认证器和用户的权限认证器,我们需要自己来实现Realm来自定义的管理我们自己系统内部的权限规则。SecurityManager要验证用户,需要从Realm中查询,它可以算是安全上的DAO,其封装了很多的类似数据库连接和数据源。
主要有以下几个功能点:
Authentication:身份认证(登录);
Authorzation:授权,判断用户是否由某个具体的权限;
Session Manager:会话管理;
Cryptography:加密,保护数据安全性,敏感数据密文存储;
WebSupport:web项目支持;
Caching:缓存,部分场景下提高数据存取效率;
Concurrency:多应用多线程的并发管理(权限共享);
RememberMe:配置后可以自动登陆。
配置文件
可以使用ini配置,但是在Spring项目中,这并不是最佳的配置方案。手动配置如下:
xml文件常规配置
1.配置web.xml文件
<!-- Shiro filter--> <filter> <filter-name>shiroFilter</filter-name> <filter-class> org.springframework.web.filter.DelegatingFilterProxy </filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
2.配置applicationContext.xml(略去部分内容)
<!-- 继承自AuthorizingRealm的自定义Realm,即指定Shiro验证用户登录的类为自定义的ShiroDbRealm.java --> <bean id="myRealm" class="com.jadyer.realm.AuthRealm"/> <!-- 设置自定义的Realm应用 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="myRealm"/> <property name="cacheManager" ref="shiroCacheManager"/> <property name="rememberMeManager" ref="shiroRemembarMeManager"/> <property name="realm" ref="myRealm"/> </bean> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 安全管理器 --> <property name="securityManager" ref="securityManager" /> <!-- 一些操作的url --> <property name="loginUrl" value="/login" /> <property name="successUrl" value="/login/loginSuccessFull" /> <property name="unauthorizedUrl" value="/login/unauthorized" /> <!-- 自定义filter配置 --> <property name="filters"> <map> <entry key="authc"> <bean class="com.onefish.filter.CustomFormAuthenticationFilter"></bean> </entry> </map> </property> <!-- anon不需要认证 authc需要认证 user 验证通过或者rememberMe可以 --> <property name="filterChainDefinitions"> <value> /home* = anon / = anon /site/** = user /logout = logout /role/** = roles[admin,system] /vendor/** = perms[permssion:look] /** = authc </value> </property> </bean>
Spring boot项目中的配置
/** * shiro的配置类 * @author Administrator * */ @Configuration public class ShiroConfiguration { @Bean(name = "shiroFilter") public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(manager); //配置登录的url和登录成功的url bean.setLoginUrl("/login"); bean.setSuccessUrl("/"); //配置访问权限 LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/tool", "anon"); //配置权限 filterChainDefinitionMap.put("/blogEditor", "anon"); filterChainDefinitionMap.put("/templates/*.*", "authc"); filterChainDefinitionMap.put("/logout", "logout"); //登出接口,自带过滤器已经实现基本的信息清除 bean.setFilterChainDefinitionMap(filterChainDefinitionMap); return bean; } //配置核心安全事务管理器 @Bean(name = "securityManager") public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) { System.err.println("--加载shiro--"); DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(authRealm); return manager; } //配置自定义的权限登录器 @Bean(name = "authRealm") public AuthRealm authRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) { AuthRealm authRealm = new AuthRealm(); authRealm.setCredentialsMatcher(matcher); return authRealm; } //配置自定义的密码比较器 @Bean(name = "credentialsMatcher") public CredentialsMatcher credentialsMatcher() { return new CredentialsMatcher(); } @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(true); return creator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager manager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(manager); return advisor; } }
重写Realm
重写Realm类,指定授权和认证的方式 下面是一个最简单的Realm
@Service public class AuthRealm extends AuthorizingRealm { @Autowired private UserService userService; //认证.登录 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken utoken=(UsernamePasswordToken) token;//获取用户输入的token String username = utoken.getUsername(); User user = userService.findUserByName(username); if(user!=null) { return new SimpleAuthenticationInfo(user, user.getPswd(), this.getClass().getName()); //放入shiro.调用CredentialsMatcher检验密码 //注意这里 第一个对象是放入session用于后面校验用户的 在这选取了整个user对象 }else{ return null; } } //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) { User user=(User) principal.fromRealm(this.getClass().getName()).iterator().next();//获取session中的用户 List<String> permissions=new ArrayList<>(); Set<Role> roles = user.getRoles(); if(roles.size()>0) { for(Role role : roles) { /* Set<Module> modules = role.getModules(); if(modules.size()>0) { for(Module module : modules) { permissions.add(module.getMname()); } } */ permissions.add(role.getName()); } } SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); info.addStringPermissions(permissions);//将权限放入shiro中. return info; } }
重写、添加其余依赖
重写密码比较器
@Service public class CredentialsMatcher extends SimpleCredentialsMatcher { @Autowired EncodeFacade encodeFacade; @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { UsernamePasswordToken utoken=(UsernamePasswordToken) token; //获得用户输入的密码 String inPassword = new String(utoken.getPassword()); //获得数据库中的密码 String dbPassword=(String) info.getCredentials(); //进行密码的比对 encodeFacade.checkPsw(inPassword,dbPassword,utoken.getUsername()); return this.equals(inPassword, dbPassword); }
编写登录接口
写登陆的认证,注意此处异常的处理
@RequestMapping(value="/login",method=Request.POST) public String login(@RequestBody User user){ Subject subject=SecurityUtils.getSubject(); UsernamePasswordToken userToken=new UsernamePasswordToken(user.getName(),user.getPassword()); try{ subject.login(userToken); return "登陆成功"; }catch (UnknownAccountException e){ return "用户名不存在"; }catch (IncorrectCredentialsException e){ return "密码错误"; } }
注意各异常的分类和异常的捕获处理:
均是AuthenticationException的子类异常:
UnknownAccountException:错误的账号;
LockedAccountException:锁定的账号;
DisabledAccountException:禁用的账号;
IncorrctCredentialsException:错误的凭证;
ExpiredCredentialsException:过期的凭证;
ExcessiveAttemptsEXception:失败次数过多;
前端页面此处略
其他操作
一些常用操作: 获取当前subject(用户)标识,即前面new SimpleAuthenticationInfo中第一个参数
(User)SecurityUtils.getSubject().getPrincipal();
Spring Security
与Spring系列框架期和良好,功能较为强大。
maven引入
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>{spring-security-version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>{spring-security-version}</version> </dependency>
定义认证逻辑
配置Config类,继承WebSecurityConfigurerAdapter类,并重写该类原有部分方法
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override // protected void configure(AuthenticationManagerBuilder auth){ super.configure(auth); } @Override protected void configure(HttpSecurity http){ super.configure(http); } @Override protected void configure(WebSecurity web){ super.configure(web); } }
重写
@Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() // 定义当需要用户登录时候,转到的登录页面 .and() .authorizeRequests() // 定义哪些URL需要被保护、哪些不需要被保护 .anyRequest() // 任何请求,登录后可以访问 .authenticated(); }
参考链接:权限控制框架-shiro