String username = authenticationToken.getPrincipal().toString();
if(!username.equals(“zhang”)){
throw new UnknownAccountException(“此用户不存在”);
}
//返回验证信息,参数:1、用户名 2、正确密码 3、realm名称
return new SimpleAuthenticationInfo(username,“123”, getName());
}
}
2) 使用自定义Realm进行登录和授权
//创建默认安全管理器
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//安全管理器配置自定义Realm
securityManager.setRealm(new MyRealm());
//SecurityUtils配置安全管理器
SecurityUtils.setSecurityManager(securityManager);
//获得Subject对象
Subject subject = SecurityUtils.getSubject();
//创建账号密码token
UsernamePasswordToken user = new UsernamePasswordToken(“zhang”, “123”);
//登录验证
subject.login(user);
//权限判断
System.out.println(“是否拥有role1角色:” + subject.hasRole(“role1”));
System.out.println(“是否拥有delete权限:” + subject.isPermitted(“user:delete”));
[](
)密码加密加盐
====================================================================
用户密码一般不会以明文方式保存,这样无法保证安全性,所以一般都需要加密。
SimpleHash类可以实现基本的加密,几种创建方式:
new SimpleHash(“加密算法”,“原始密码”)
new SimpleHash(“加密算法”,“原始密码”,盐)
new SimpleHash(“加密算法”,“原始密码”,盐,迭代次数)
参数说明:
加密算法一般使用常用的md5算法
盐的作用是提高密码安全性,如两个用户的原始密码都是123,则加密后的密文都是相同的,如果破解了一个用户的密码,另一个用户的密码也一同破解了,如果给密码加盐,每个用户的盐不同,加密后密码就都会不同,增加了破解难度。
迭代次数是加密一次后,再对密文再次加密,也能提高安全性。
下面我们以md5算法对“123456”加密,盐是“007”,迭代次数为10。
SimpleHash md5 = new SimpleHash(“md5”, “123456”,ByteSource.Util.bytes(“007”), 10);
System.out.println(md5);
输出:44202d045439dc33a2e43d2828d08e19
修改MyRealm的doGetAuthenticationInfo方法,这里将密文和盐直接写在代码中,实际应用时密文和盐是通过用户名从数据库中查询出来的。
//返回验证信息,参数:1、用户名 2、正确密码 3、盐 4、realm名称
return new SimpleAuthenticationInfo(username,“44202d045439dc33a2e43d2828d08e19”, ByteSource.Util.bytes(“007”),getName());
需要给自定义Realm添加密码匹配器
//创建默认安全管理器
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//安全管理器配置自定义Realm
MyRealm realm = new MyRealm();
//创建密码匹配器
HashedCredentialsMatcher md5 = new HashedCredentialsMatcher(“md5”);
//设置迭代次数
md5.setHashIterations(10);
//配置匹配器
realm.setCredentialsMatcher(md5);
securityManager.setRealm(realm);
//SecurityUtils配置安全管理器
SecurityUtils.setSecurityManager(securityManager);
//获得Subject对象
Subject subject = SecurityUtils.getSubject();
//创建账号密码token
UsernamePasswordToken user = new UsernamePasswordToken(“zhang”, “123456”);
//登录验证
subject.login(user);
//权限判断
System.out.println(“是否登录成功:” + subject.isAuthenticated());
System.out.println(“是否拥有role1角色:” + subject.hasRole(“role1”));
System.out.println(“是否拥有delete权限:” + subject.isPermitted(“user:delete”));
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VzpaSypf-1608710040672)(shiro.assets/1608618247076.png)]](https://img-blog.csdnimg.cn/20201223155935569.png#pic_center)
[](
)SpringBoot+MyBatis+Shiro整合
========================================================================================
1、表设计
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XS3cRTkB-1608710040674)(shiro.assets/1608618965879.png)]](https://img-blog.csdnimg.cn/20201223155947307.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTMzNDMxMTQ=,size_16,color_FFFFFF,t_70#pic_center)
s_user 用户表
s_role 角色表
s_menu 菜单表(权限表)
s_user_menu 用户角色中间表
s_role_menu 角色菜单中间表
2、添加依赖
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
com.baomidou
mybatis-plus-boot-starter
3.3.2
mysql
mysql-connector-java
8.0.16
org.apache.shiro
shiro-spring-boot-web-starter
1.7.0
org.springframework.boot
spring-boot-starter-thymeleaf
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0
2、SpringBoot配置
jdbc配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/erp_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456
mybatis配置
mybatis-plus.type-aliases-package=com.blb.blb_erp.entity
mybatis-plus.mapper-locations=classpath:mapper/*.xml
shiro配置
登录页面
shiro.loginUrl=/pages/login.html
登录失败跳转页面
shiro.unauthorizedUrl=/pages/failed.html
登录成功跳转页面
shiro.successUrl=/pages/index.html
3、编写Mapper接口
需要三个方法:
按用户名查找用户
按用户id查询所有菜单
按用户id查询所有角色
/**
- 用户接口
*/
public interface SUserMapper extends BaseMapper{
/**
通过用户名查询用户
@param username
@return
*/
SUser selectUserByUsername(String username);
}
映射文件:
<?xml version="1.0" encoding="UTF-8"?>select * from s_user where user_name = #{username}
/**
- 菜单接口
*/
public interface SMenuMapper extends BaseMapper{
/**
根据userId查询所有权限
@param userId
@return
*/
List selectMenusByUserId(String userId);
}
映射文件:
<?xml version="1.0" encoding="UTF-8"?>select m.* from s_user u,s_role r,s_user_role ur,s_menu m,s_role_menu rm
where ur.role_id = r.id and ur.user_id = u.id and rm.role_id = r.id and rm.menu_id = m.id
and u.id = #{userId}
/**
- 角色接口
*/
public interface SRoleMapper extends BaseMapper{
/**
根据用户id查询所有角色
@param userId
@return
*/
List selectRolesByUserId(String userId);
}
映射文件:
<?xml version="1.0" encoding="UTF-8"?>select r.* from s_user_role ur
join s_user u on ur.user_id = u.id
join s_role r on ur.role_id = r.id
where ur.user_id = #{userId}
4、自定义Realm
/**
- 用户Realm
*/
public class UserRealm extends AuthorizingRealm {
@Autowired
private SUserMapper userMapper;
@Autowired
private SRoleMapper roleMapper;
@Autowired
private SMenuMapper menuMapper;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获得用户对象
SUser user = (SUser) principalCollection.getPrimaryPrincipal();
//查询权限和角色
List menus = menuMapper.selectMenusByUserId(user.getId());
List roles = roleMapper.selectRolesByUserId(user.getId());
//保存权限和角色名称的集合
List strRoles = new ArrayList<>();
roles.forEach(r -> strRoles.add(r.getRoleName()));
List strMenus = new ArrayList<>();
menus.forEach(m -> strMenus.add(m.getMenuName()));
//返回带有角色和权限名称的授权信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(strRoles);
info.addStringPermissions(strMenus);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获得账号
String username = authenticationToken.getPrincipal().toString();
//通过账号查询用户
SUser user = userMapper.selectUserByUsername(username);
if(user == null){
throw new UnknownAccountException(“此用户不存在”);
}
//返回验证信息 参数:1、用户对象 2、正确密码 3、盐 4、realm名称
return new SimpleAuthenticationInfo(user,user.getPassWord(), ByteSource.Util.bytes(user.getSalt()),getName());
}
}
5、Shiro配置类
/**
- Shiro配置
*/
@Configuration
public class ShiroConfig {
//返回Realm
@Bean
public UserRealm myRealm(){
UserRealm myRealm = new UserRealm();
//设置密码匹配器
HashedCredentialsMatcher md5 = new HashedCredentialsMatcher(“md5”);
md5.setHashIterations(10);
myRealm.setCredentialsMatcher(md5);
//关闭缓存
myRealm.setCachingEnabled(false);
return myRealm;
}
//返回面向Web开发的安全管理器
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
//设置自定义Realm
sm.setRealm(myRealm());
return sm;
}
//返回Shiro过滤器链定义
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
//定义过滤器链key为url,value为anon不验证,authc验证
//anon在前,authc在后,需要使用LinkedHashMap保留顺序
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put("/pages/login.html",“anon”);
map.put("/user/login",“anon”);
map.put("/**",“authc”);
chainDefinition.addPathDefinitions(map);
return chainDefinition;
}
//启动thymeleaf的shiro标签
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
6、启动类
@MapperScan(“com.blb.blb_erp.mapper”)
@SpringBootApplication
public class BlbErpApplication {
public static void main(String[] args) {
SpringApplication.run(BlbErpApplication.class, args);
}
}
7、控制器
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonResult {
private int code;
private Object data;
}
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/login")
public JsonResult login(String username,String password){
//创建Token
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//获得subject
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
return new JsonResult(1,“登录成功”);
}catch (AuthenticationException ex){
ex.printStackTrace();
}
return new JsonResult(0,“账号或密码错误”);
}
@RequiresRoles(“管理员”)
@GetMapping("/role-admin")
public String testRole(){
return “有管理员角色”;
}
@RequiresPermissions(“部门管理”)
@GetMapping("/menu-dept")
public String testMenu(){
return “有部门管理权限”;
}
}
@RequiresRoles、@RequiresPermissions写在控制器的方法上,登录用户有对应的角色和权限才能访问。
[](
)RememberMe
========================================================================
可以在登录页面上添加记住我功能,勾选后下次不用登录直接进去系统了
1、页面上添加RememberMe复选框