记录着玩。最近搭建了一套Spring boot的项目,所以想着搞一个安全框架,所以就搭建了一套shiro框架。
第一步:在pom文件中引入shiro的包
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
第二部:创建shiro的conf文件和customerRealm文件
第一个是shiroConf文件
package com.example.springboot.config;
import com.example.springboot.shiro.CustomRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
//将自己的验证方式加入容器
@Bean(name = "CustomRealm")
public CustomRealm myShiroRealm() {
CustomRealm customRealm = new CustomRealm();
return customRealm;
}
//权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
//Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
map.put("/login","anon");
map.put("/doc.html", "anon");
map.put("/swagger-resources/**", "anon");
map.put("/v2/api-docs", "anon");
map.put("/v2/api-docs-ext", "anon");
map.put("/webjars/**", "anon");
map.put("/**", "authc");
// //登录
shiroFilterFactoryBean.setLoginUrl("/login");
// //首页
// shiroFilterFactoryBean.setSuccessUrl("/index");
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
第二个是customRealm
package com.example.springboot.shiro;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.springboot.entity.UserEntity;
import com.example.springboot.service.UserService;
import com.example.springboot.utils.PasswordEncryption;
import lombok.SneakyThrows;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.Set;
public class CustomRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String) SecurityUtils.getSubject().getPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> stringSet = new HashSet<>();
stringSet.add("user:show");
stringSet.add("user:admin");
info.setStringPermissions(stringSet);
return info;
}
/**
* shiro认证账号密码
* <p>
* 获取即将需要认证的信息
*/
@SneakyThrows
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("-------身份认证方法--------");
String userName = (String) authenticationToken.getPrincipal();
String userPwd = new String((char[]) authenticationToken.getCredentials());
//根据用户名从数据库获取密码
UserEntity one = userService.getOne(new LambdaQueryWrapper<UserEntity>().eq(UserEntity::getUserName, userName));
if (null == one)
throw new UnknownAccountException("账号不存在");
if (one.getStatus().equals("0"))
throw new LockedAccountException("用户被锁定");
if (!PasswordEncryption.authenticate(userPwd, one.getPassword(), one.getMD5()))
throw new IncorrectCredentialsException("密码错误");
//将用户数据存储在session中
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("userSession", one);
SecurityUtils.getSubject().getSession().setTimeout(1000 * 60);//设置session过期时间
return new SimpleAuthenticationInfo(userName, userPwd, getName());
}
}
在customRealm中要重写doGetAuthenticationInfo方法作为登陆验证SecurityUtils.getSubject().getSession().setTimeout(1000 * 60);这句可以设置session的超时时间,单位是毫秒。
//将用户数据存储在session中 Session session = SecurityUtils.getSubject().getSession(); session.setAttribute("userSession", one);
上面两句则创建一个session并且将用户实体类one存储到seeeion中。
可以将存储的用户实体写作一个公共的controller,并且每一个controller去继承。
package com.example.springboot.controller;
import com.example.springboot.entity.UserEntity;
import org.apache.shiro.SecurityUtils;
public class BaseController {
public UserEntity getUserSession() {
UserEntity userSession = (UserEntity) SecurityUtils.getSubject().getSession().getAttribute("userSession");
return userSession;
}
}
接下来贴出登陆接口。
@ApiOperation(value = "登陆")
@PostMapping(value = "/login")
public Result login(@RequestBody UserEntity user) {
if (StringUtils.isNotBlank(user.getUserName()) && StringUtils.isNotBlank(user.getPassword())) {
try {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUserName(), user.getPassword());
subject.login(usernamePasswordToken);
} catch (UnknownAccountException e) {
return Result.error(CodeMsg.USERERROR, "账号不存在");
} catch (LockedAccountException lock) {
return Result.error(CodeMsg.USERERROR, "用户被锁定");
} catch (IncorrectCredentialsException exception) {
return Result.error(CodeMsg.USERERROR, "密码错误");
}
return Result.success("登陆成功");
}
return Result.error(CodeMsg.USERERROR, "用户名密码不能为空");
}
subject.login会调用到customRealm中重写的doGetAuthenticationInfo方法。
以上内容都是个人瞎记录,有什么问题请大佬们指出。