Shiro 学习之登录以及记住密码功能的实现

时间:2019-02-14
本文章向大家介绍Shiro 学习之登录以及记住密码功能的实现,主要包括Shiro 学习之登录以及记住密码功能的实现使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

学了一段时间的Spring Security,学的一脸懵逼,除了会用加密,其余的没整明白。

偶尔发现了shiro,学了一下,觉的比Spring Security简单一些,起码能用明白

这里就记录一下如何用shiro进行用户身份验证,权限的验证还有记住密码的功能

首先是导入

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

那么接下来就是进行Security Manager相关的配置了

Security Manager管理所有用户的安全操作,它是shiro的核心,这里贴出我配置的Security Manager

@Configuration
public class ShiroConfig {
	/**
	 * Filter工厂,设置对应的过滤条件和跳转条件
	 * @param securityManager
	 * @return
	 */
	
	@Bean
	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		//登录,作用就是,对于一些需要验证的页面,如果验证没有通过,就会跳转到这个url,如果不设置的话,默认是Login.jsp,可以自己设置任意一个url
		/*shiroFilterFactoryBean.setLoginUrl("/login");*/
		
		//无权限跳转的页面
		shiroFilterFactoryBean.setUnauthorizedUrl("/login/403");
		
		/*Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
		filters.put("authc",  ajaxFilter());*/
		
		//权限控制的Map
		Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
		
		//对首页,登录,注册以及静态资源不设置权限访问
		filterChainDefinitionMap.put("/jq/**", "anon");
		filterChainDefinitionMap.put("/layer/**", "anon");
		filterChainDefinitionMap.put("/", "anon");
		filterChainDefinitionMap.put("/login", "anon");
		filterChainDefinitionMap.put("/login/user-check", "anon");
		//admin 需要ADMIN角色
		filterChainDefinitionMap.put("/login/admin", "roles[ADMIN]");
		//user 需要USER角色
		filterChainDefinitionMap.put("/login/user", "roles[USER]");
		//
		filterChainDefinitionMap.put("/login/youke", "anon");
		//对于注册,不需要任何权限 anon
		filterChainDefinitionMap.put("/login/register-show", "anon");
		filterChainDefinitionMap.put("/login/register-register", "anon");
		//退出,拦截/login/check-play  Filter : LogoutFilter 默认跳转的url: DEFAULT_REDIRECT_URL = "/"
		filterChainDefinitionMap.put("/login/check-play", "logout");
		//对所有请求进行拦截 Filter : FormAuthenticationFilter
		//对/**的设置最好要放到最后
		filterChainDefinitionMap.put("/**", "user");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		System.out.println("Shiro拦截器工厂注入成功");
		return shiroFilterFactoryBean;
	}
	/**
	 * 权限管理
	 * @return
	 */
	@Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		//开启密码与去权限验证
		securityManager.setRealm(customRealm());
		//开启记住我
		securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}
	
	/**
	 * 自己的验证方式
	 * @return
	 */
	
	 @Bean
	 public CustomRealm customRealm() {
	     return new CustomRealm();
	 }
	 
	 @Bean
	 public AjaxFilter ajaxFilter() {
		 return new AjaxFilter();
	 }
	 
	/* @Bean
	 public FilterRegistrationBean register(AjaxFilter ajaxFilter) {
		 FilterRegistrationBean register = new FilterRegistrationBean(ajaxFilter);
		 register.setEnabled(false);
		 return register;
	 }*/
	 
	 /**
	  * 记住密码 相关操作
	  * @return
	  */
	 @Bean
	 public SimpleCookie rememberMeCookie() {
		 //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
		 SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
		 //设置记住我的时间 单位是秒 但目前木有实现 不知道怎么回事
		 simpleCookie.setMaxAge(30);
		 return simpleCookie;
	 }
	 
	 /**
	  * 记住密码 相关操作
	  * @return
	  */
	 @Bean
	 public CookieRememberMeManager rememberMeManager() {
		 CookieRememberMeManager  cookieRememberMeManager = new CookieRememberMeManager();
		 cookieRememberMeManager.setCookie(rememberMeCookie());
		 return cookieRememberMeManager;
	 }
	 
	 /**
	  * 记住密码 相关操作
	  * @return
	  */
	 @Bean
	 public FormAuthenticationFilter formAuthenticationFilter() {
		 FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
		 formAuthenticationFilter.setRememberMeParam("rememberMe");
		 return formAuthenticationFilter;
	 }
	 
	 

	 /**
	  * 定义 DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator 与 AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
	  * 是为了让shiro的 注解生效 @RequiresRoles 与 @RequiresPermissions 
	  * @return
	  */
	 @Bean
	 public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
	     DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
	     advisorAutoProxyCreator.setProxyTargetClass(true);
	     return advisorAutoProxyCreator;
	 }
	  
	 /**
	  * 定义 DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator 与 AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
	  * 是为了让shiro的 注解生效 @RequiresRoles 与 @RequiresPermissions 
	  * @return
	  */
	 @Bean
	 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
	     AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
	     authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
	     return authorizationAttributeSourceAdvisor;
	 }
	

}

身份验证

在shiro中,用户需要提供principals(身份)和credentials(证明)给shiro,用于验证身份,最常见的principals与credentials组合就是用户的账号/密码了

身份认证流程

@PostMapping(value = "/user-check")
	public String loginCheck(User user,Model model,boolean rememberMe) {
		System.out.println(rememberMe);
		Subject subject = SecurityUtils.getSubject();
		//对登录情况进行判断
		if(subject.isAuthenticated()) {
			System.out.println("已经登录了");
			return "403";
		}
		UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(),user.getPassword(),rememberMe);
		try {
		    subject.login(usernamePasswordToken);
		}catch(IncorrectCredentialsException e) {
			model.addAttribute("message", "密码错误");
			return "login";
		}catch(AuthenticationException e) {//在这里捕捉异常
			model.addAttribute("message", "登录失败");
			return "login";
		}
		return "home";
	}

SecurityManager负责真正的身份验证逻辑,就是这个,主要是setRealm()这个方法,就是将自己的验证方式(Relam)告诉SecurityManager

Realm:域

Shiro从Realm获取安全数据(如用户,角色,权限),就是说SecurityManager要验证用户身份,那么它必须要从Relam获取相应的用户进行比较以确定用户是否合法

@Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		//开启密码与去权限验证
		securityManager.setRealm(customRealm());
		//开启记住我
		securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}

下面是我自己的Relam

public class CustomRealm extends AuthorizingRealm{
	
	Logger log = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	private UserMapper userMapper;

	/**
	 * 角色与权限的验证
	 * 一个用户一般来说只有一个角色,但会有很多权限
	 * 
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		log.info("权限认证方法");
		String username = (String) SecurityUtils.getSubject().getPrincipal();
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		User user = userMapper.selectUser(username);
		//获取角色
		String role = user.getUrole();
		System.out.println(role);
		info.addRole(role);
		//一般来说,每一个角色都会有一些权限
		//获取用户的角色权限,但我没有设置,所以就省略了
		//List<Role> roleList=user.getRoleList();  
        //for (Role role : roleList) {  
            //info.addStringPermissions(role.getPermissionsName());  
        //}  
		return info;
	}

	/**
	 * 身份验证
	 * 获取登录的信息,将信息与数据库中的信息进行对比,就是普通的登录验证
	 * 然后将登录的信息(账号,密码)放进SimpleAuthenticationInfo中,用于权限的验证
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		log.info("身份认证方法");
		UsernamePasswordToken token = (UsernamePasswordToken) arg0;
		User user = userMapper.selectUser(token.getUsername());
		String password = new String((char[])token.getCredentials());
		if(user == null) {
			throw new AccountException("该用户不存在");
		}else {
			if(!user.getPassword().equals(password)) {
				//这里抛出异常就是为了controller中接受这个异常,用来判断登录状况
				throw new IncorrectCredentialsException("密码不正确");
			}
		}
		return new SimpleAuthenticationInfo(token.getPrincipal(),password,getName());
	}

}
//相应的异常
//DisabledAccountException(禁用的帐号)
//LockedAccountException(锁定的帐号)
//UnknownAccountException(错误的帐号)
//ExcessiveAttemptsException(登录失败次数过多)
//IncorrectCredentialsException (错误的凭证)
//ExpiredCredentialsException(过期的凭证)

上边的这些代码居包括了权限认证,身份认证以及记住密码

关于ajax权限的验证,以及登录次数的限制,目前就想到了这些,等弄明白了在总结一下