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权限的验证,以及登录次数的限制,目前就想到了这些,等弄明白了在总结一下
- es6 class
- WiX安装选项---开始菜单项
- 代码编辑器Sublime Text 3 免费使用方法与简体中文汉化包下载
- TCP/IP 选项TcpTimedWaitDelay设置
- Windows 远程管理WinRM
- Sublime text 3 中Package Control 的安装与使用方法
- 用functools.lru_cache实现Python的Memoization
- 腾讯互联网与社会研究院秘书长司晓:将联合开展研究合作
- 幻灯片jQuery插件Orbit 介绍(附添加到WordPress教程)
- Gravatar开发者手册
- Gravatar开发者手册
- 使用Google CDN服务提供的jQuery库
- 比特币的分叉币都认为能够取代比特币,事实真的是这样吗?
- Google官方网页载入速度检测工具PageSpeed Insights 使用教程
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法