通过鸟哥文章学习Shiro知识,原文连接:https://mrbird.cc/Spring-Boot-shiro%20Authentication.html
1. 搭建一个Spring Boot Web,然后引入Shiro
本篇不讲解Spring Boot Web项目搭建,直接在一个Spring Boot Web 项目基础上引入Shiro并做用户认证配置。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
2. 定义一个Shiro配置类
定义filterChain配置不配拦截的url
配置退出过滤器,具体代码实现Shiro已有
除配置了不拦截的url以外其他url都必须通过认证才能进行访问
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 登录的url
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后跳转的url
shiroFilterFactoryBean.setSuccessUrl("/index");
// 未授权url
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 定义filterChain,静态资源不拦截
filterChainDefinitionMap.put("/febs/**", "anon");
filterChainDefinitionMap.put("/layui/**", "anon");
// druid数据源监控页面不拦截
filterChainDefinitionMap.put("/druid/**", "anon");
// 配置退出过滤器,其中具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/", "anon");
// 除上以外所有url都必须认证通过才可以访问,未通过认证自动访问LoginUrl
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public DefaultWebSecurityManager securityManager(){
// 配置SecurityManager,并注入shiroRealm
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
return securityManager;
}
@Bean
public ShiroRealm shiroRealm(){
// 配置Realm,需自己实现
ShiroRealm shiroRealm = new ShiroRealm();
return shiroRealm;
}
}
3. 配置完ShiroConfig后,接下来对Realm进行实现,然后注入到SecurityManager中。
doGetAuthorizationInfo:获取用户角色和权限(本篇只讲解验证)
doGetAuthenticationInfo:对登录的账号密码进行验证
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private BaseUserService baseUserService;
/**
* 获取用户角色和权限
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
return null;
}
/**
* 登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取用户输入的用户名和密码
String userName = (String) token.getPrincipal();
String password = new String((char[]) token.getCredentials());
System.out.println("用户" + userName + "认证-----ShiroRealm.doGetAuthenticationInfo");
// 通过用户名到数据库查询用户信息
BaseUser user = baseUserService.findByName(userName);
if (user == null) {
throw new UnknownAccountException("用户名或密码错误!");
}
if (!password.equals(user.getPassword())) {
throw new IncorrectCredentialsException("用户名或密码错误!");
}
/*if (user.getStatus().equals("0")) {
throw new LockedAccountException("账号已被锁定,请联系管理员!");
}*/
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
return info;
}
}
4. 密码存储通过MD5加密处理
这里用的是一般加盐处理,普通的用户名+密码再MD5
public class Md5Util {
private static final String ALGORITHM_NAME = "md5";
private static final int HASH_ITERATIONS = 5;
public static String encrypt(String username, String password) {
String source = StringUtils.lowerCase(username);
password = StringUtils.lowerCase(password);
return new SimpleHash(ALGORITHM_NAME, password, ByteSource.Util.bytes(source), HASH_ITERATIONS).toHex();
}
}
5.Controller
获取到用户输入的用户名和密码后,对其进行验证
UsernamePasswordToken 是用来存储用户名和密码
@PostMapping("/login")
public Response login(String username, String password){
//密码MD5加密
password = Md5Util.encrypt(username, password);
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//获取Subject对象
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
return new Response().success();
} catch (AuthenticationException e) {
return new Response().error().data("认证失败!");
} catch (Exception e){
return new Response().error().data(e.getMessage());
}
}