一步一步教你用shiro——2配置并自定义realm

2018-04-30  本文已影响1364人  江江的大猪

shiro中realm的是进行认证和授权的组件,自带了几种实现,比如jdbcRealm和iniRealm,实际项目中肯定都是自己实现realm

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_name` varchar(10) NOT NULL DEFAULT '' COMMENT '用户名',
  `password` varchar(50) NOT NULL DEFAULT '' COMMENT '用户密码,用户名为盐,五次md5',
  `roles` varchar(20) NOT NULL DEFAULT '' COMMENT '角色名,逗号分隔',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_idx_role_name` (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
@Service
public class UserService {
    //mybatis代理实现的dao
    @Resource
    private UserDao userDao;

    //根据用户名获得user对象
    public User queryUserByName(String name) {
        try {
            if (StringUtils.isBlank(name)) {
                return null;
            }
            return userDao.queryUserByName(name);
        } catch (Exception e) {
            log.error("db error when query user:{}", name, e);
        }
        return null;
    }
    
    //根据用户名获得用户的所有角色
    public Set<String> queryUserRole(String userName) {
        User user = queryUserByName(userName);
        if (user == null) {
            return Collections.emptySet();
        }
        List<String> roleList = StringAssist.splitComma(user.getRoles());
        return Sets.newHashSet(roleList);
    }
}
     @PostMapping("login")
    public String login(String username, String password) {
        try {
            //shiro通过SecurityUtils.getSubject()获得主体,主体可以理解为客户端实例,原理在后面讲
            Subject subject = SecurityUtils.getSubject();
            //已经认证过,也就是该客户端已经登陆过
            if (subject.isAuthenticated()) {
                return "redirect:/static/html/indexLogin.html";
            }
            //一般都使用UsernamePasswordToken,shiro的token中有Principal和Credentials的概念
            //Principal代表当前客户端要登录的用户,Credentials代表证明该用户身份的凭证
            //UsernamePasswordToken将username作为Principal,password作为Credentials
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            //rememberMe功能后面讲
            token.setRememberMe(true);
            subject.login(token);
        } catch (AuthenticationException e) {
            //登录失败则跳转到登录失败页面,可能是用户名或密码错误
            return "redirect:/static/html/loginError.html";
        }
        return "redirect:/static/html/indexLogin.html";
    }
public class MyRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    //用户的权限信息包含roles角色和permission权限两部分,我这里只使用了角色进行进行权限控制
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //principals.getPrimaryPrincipal()获得的就是当前用户名
        if (principals == null || StringUtils.isBlank((String) principals.getPrimaryPrincipal())) {
            return null;
        }
        //将用户角色信息传入SimpleAuthorizationInfo
        return new SimpleAuthorizationInfo(userService.queryUserRole((String) principals.getPrimaryPrincipal()));
    }

    //token实际就是在login时传入的UsernamePasswordToken
    //getPrincipal()中只执行了getUsername(),getCredentials()只执行了getPassword()
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        if (token == null||StringUtils.isBlank((String) token.getPrincipal())) {
            return null;
        }
        //根据token中的用户名查库,获得user对象
        User user = userService.queryUserByName((String) token.getPrincipal());
        if (user == null) {
            return null;
        }
        //SimpleAuthenticationInfo代表该用户的认证信息,其实就是数据库中的用户名、密码、加密密码使用的盐
        //存在数据库中的密码是对用户真是密码通过md5加盐加密得到的,保证安全,及时数据泄露,也得不到真正的用户密码
        //getName()返回该realm的名字,代表该认证信息的来源是该realm,作用不大,一般都是单realm
        //该方法返回后,上层会对token和SimpleAuthenticationInfo进行比较,首先比较Principal(),然后将token的Credentials
        //进行md5加上SimpleAuthenticationInfo中的盐加密,加密结果和SimpleAuthenticationInfo的Credentials比较
        return new SimpleAuthenticationInfo(
                user.getUserName(), user.getPassword(), ByteSource.Util.bytes(user.getUserName()), getName());
    }
    <!--非web环境使用DefaultSecurityManager-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--多realm的授权策略设置,配置为必须满足全部realm才算成功,不在realms前配置的话authenticator中的realms集合为空-->
        <!--securityManager注入realms的时候,会把realm也放一份到authenticator中,所以必须写在realms上面!!!-->
        <property name="authenticator">
            <bean class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
                <property name="authenticationStrategy">
                    <bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"/>
                </property>
            </bean>
        </property>
        <!--如果只有一个realm的话,可以直接注入realm属性,不需要注入realms属性-->
        <!--为了以后的扩展,即使只有一个realm还是注入了realms属性(虽然以后估计也都是单realm)-->
        <property name="realms">
            <list>
                <bean class="com.qunar.lfz.shiro.MyRealm">
                    <property name="credentialsMatcher">
                        <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                            <!--指定加密算法-->
                            <property name="hashAlgorithmName" value="MD5"/>
                            <!--指定对密码连续进行5轮md5加密-->
                            <property name="hashIterations" value="5"/>
                        </bean>
                    </property>
                </bean>
            </list>
        </property>
    </bean>
上一篇下一篇

猜你喜欢

热点阅读