Shiro学习笔记
入门代码
public void ShiroDemoByIni(){
IniSecurityManagerFactory iniSecurityManagerFactory = new IniSecurityManagerFactory("classpath:user.ini");
SecurityManager instance = iniSecurityManagerFactory.getInstance();
SecurityUtils.setSecurityManager(instance);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("lxy", "lxy");
subject.login(usernamePasswordToken);
System.out.println(subject.isAuthenticated());
}
ini配置
示例代码:
[users]
lxy=lxy,user
xlr=xlr,admin,user
[roles]
user="user:update,select"
知识点:
[users]下面配置用户的格式:用户名=密码,角色1,角色2,角色3......,比如xlr=xlr,admin,user
[roles]下面配置角色的格式:
1、角色=权限1,权限2,权限3......,比如admin=user:update,user:select,即admin拥有对user的update和select操作权限
2、角色="权限1,权限2,权限3......",比如admin="user:update,select",即admin拥有对user的update和select操作权限
3、角色=权限1:*,比如admin=user:*,即admin拥有对user的所有操作权限
4、角色=*:权限,比如或者admin=*:update,即admin拥有对所有资源的update权限
“:”表示资源/操作/实例的分割
“,”表示操作的分割
“*”表示任意资源/操作/实例
如“user:*”可以匹配如“user:delete”、“user:delete”可以匹配如“user:delete:1”、
“user:*:1”可以匹配如“user:view:1”、“user”可以匹配“user:view”或“user:view:1”
等。即*可以匹配所有,不加*可以进行前缀匹配;但是如“*:view”不能匹配
“system:user:view”,需要使用“*:*:view”,即后缀匹配必须指定前缀(多个冒号就需要
多个*来匹配)
未来MD5加密的实现方法
使用用户名和密码来进行MD5加密,也可以使用UUID和密码来进行MD5加密。
对SimpleAuthenticationInfo的思考
在自定义Realm中有这样一行代码:
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
User user = userService.getUser(username);
if (user == null) {
return null;
}
//传入从数据库中取出来的用户名和密码
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),"custom");
return simpleAuthenticationInfo;
}
在new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),"custom");代码中,是如何做到不需要token来验证用户输入的用户名和密码是否正确?
生成 AuthenticationInfo 信息,交给间接父类 AuthenticatingRealm 使用 CredentialsMatcher 进行判断密码是否匹配,在下面的java代码中:
public class PasswordMatcher implements CredentialsMatcher {
private PasswordService passwordService;
public PasswordMatcher() {
this.passwordService = new DefaultPasswordService();
}
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
PasswordService service = ensurePasswordService();
Object submittedPassword = getSubmittedPassword(token);
Object storedCredentials = getStoredPassword(info);
assertStoredCredentialsType(storedCredentials);
if (storedCredentials instanceof Hash) {
Hash hashedPassword = (Hash)storedCredentials;
HashingPasswordService hashingService = assertHashingPasswordService(service);
return hashingService.passwordsMatch(submittedPassword, hashedPassword);
}
//otherwise they are a String (asserted in the 'assertStoredCredentialsType' method call above):
String formatted = (String)storedCredentials;
return passwordService.passwordsMatch(submittedPassword, formatted);
}
submittedPassword和storedCredentials分别是用户输入的密码和数据库查询到的密码,然后调用passwordService.passwordsMatch来验证。
设置对静态资源不拦截
不能够设置:
filterChainDefinitionMap.put("/static/**", "anon");
应该设置成:
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
Shiro的RequiresPermissions等注解不生效
在ShiroConfig中加上这段代码:
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public MyExceptionResolver myExceptionResolver() {
return new MyExceptionResolver();
}
shiroFilterFactoryBean.setUnauthorizedUrl("/4xx.html");不生效
方法一:创建一个class类:
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
if (e instanceof UnauthorizedException) {
ModelAndView mv = new ModelAndView("/4xx");
return mv;
}
return null;
}
}
在ShiroConfig中加上:
@Bean
public MyExceptionResolver myExceptionResolver() {
return new MyExceptionResolver();
}
方法二:
在ShiroConfig中加上
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver simpleMappingExceptionResolver=new SimpleMappingExceptionResolver();
Properties properties=new Properties();
//这里的 /unauthorized 是页面,不是访问的路径
properties.setProperty("org.apache.shiro.authz.UnauthorizedException","unauthorized");
properties.setProperty("org.apache.shiro.authz.UnauthenticatedException","unauthorized");
simpleMappingExceptionResolver.setExceptionMappings(properties);
return simpleMappingExceptionResolver;
}
Thymeleaf中使用Shiro标签
在pom.xml中加入:
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
在html中加入:
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
Shiro标签不生效
在ShiroConfig中加入:
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
Shiro的@RequiresPermissions注解任选其一
在注解上加上:
@RequiresPermissions(value = {"admin:add","user:add"},logical= Logical.OR)
Shiro中用户注册加密和用户登录加密
用户注册加密
String md5 = new SimpleHash("md5", user.getPassword(), user.getUsername(), 5).toHex();
user.setPassword(md5);
SimpleHash(加密算法,加密对象,加密盐值,加密次数)。
在获取到user对象后,即可设置加密。
用户登录加密
在ShiroConfig中配置Bean:
//设置在登录时自动将密码加密
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//设置加密算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//设置加密次数
hashedCredentialsMatcher.setHashIterations(5);
return hashedCredentialsMatcher;
}
在CustomRealm中设置:
SimpleAuthenticationInfo custom = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), "custom");
//设置用户加密的盐值,这里使用的是用户名
custom.setCredentialsSalt(ByteSource.Util.bytes(user.getUsername()));
return custom;
设置自动登录后设置不能访问login页
@GetMapping(value = "/login")
public String skipLogin(){
Subject subject = SecurityUtils.getSubject();
//设置自动登录后设置不能访问login页(判断是否登录,如果登录了,访问login页时就直接跳转到list页面)
if(subject.isAuthenticated()){
return "redirect:/list";
}else{
return "/login";
}
}